java 为什么clone()不能用于防御性复制?

slmsl1lt  于 2023-02-11  发布在  Java
关注(0)|答案(4)|浏览(134)

在《有效的Java》(第7章)中,它说
还要注意的是,我们没有使用Date的clone方法来进行防御性复制,因为Date是非final的,所以clone方法不能保证返回一个类为java.util.Date的对象:它可能返回专门为恶意破坏设计的不受信任子类的示例。例如,此类子类可能在创建时记录对私有静态列表中每个示例的引用,并允许攻击者访问此列表。这将给予攻击者可以自由支配所有示例。要防止此类攻击,不要使用clone方法对类型可被不受信任方子类化的参数进行防御性复制。
我不太明白它的解释。为什么clone()不返回Date对象?示例怎么可能是不可信子类?

vxqlmq5t

vxqlmq5t1#

请看下面的代码:

public class MaliciousDate extends Date { /** malicious code here **/ }

public class SomeClass {
    public static void main(String[] args) {
        MaliciousDate someDate = new MaliciousDate();
        Date copyOfMaliciousDate = someDate;
        Date anotherDate = copyOfMaliciousDate.clone();
    }
}

因为copyOfMaliciousDate的类型是Date,所以可以调用clone(),它将返回一个Date对象,但是在copyOfMaliciousDate上调用clone将执行在MaliciousDate类的clone()中编写的代码,因为存储在copyOfMaliciousDate中的 * instance * 是一个MaliciousDate

0vvn1miw

0vvn1miw2#

clone()被广泛认为是一个失败的实验,原因有很多,在这种情况下,传入Date的某个人可能传入了EvilDate extends Date,其clone()方法偷偷返回了一个仍然可以被其他人修改的副本。

zynd9foi

zynd9foi3#

我没有读过你引用的那本书,但那一段理由很差,而且没有提供任何保护措施来抵御任何攻击。
引文提到,能够将代码加载到程序中的攻击者可能会使用恶意方法提交Date子类,例如从clone返回Date的子类。
但这只是具有加载代码能力的攻击者造成危害的一个次要方面。他们还可能:

  • 使用反射可以获得对几乎所有标记为私有的内容的读写访问权限,
  • 干扰类装入器装入它们自己的类版本,
  • 调用System.exit()停止程序,并
  • 做你的程序能做的任何事情,比如生成其他程序或者访问文件。

如果攻击者正在您的进程中运行代码,那么游戏就结束了,您的进程也受到了危害,而这个愚蠢的小防护装置也帮不上什么忙。
也许您认为克隆从设计的Angular 来看是不好的,这没关系,但是请不要假装不使用它会保护您免受某些安全威胁,因为它不会。

cedebl8k

cedebl8k4#

由于Date类不是final类,因此可以扩展该类,并重写clone方法以存储示例,以后可以访问这些示例来修改状态。
MaliciousDate.java

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class MaliciousDate extends Date {

    private static List<MaliciousDate> instances = new ArrayList<>();

    @Override
    public Object clone() {
        instances.add(this);
        return super.clone();
    }

    public List<MaliciousDate> getInstances() {
        return instances;
    }
}

Period.java

import java.util.Date;

public class Period {
    private static Date start;
    private static Date end;

    public Period(Date start, Date end) {
        this.start = (Date) start.clone();
        this.end = (Date) end.clone();
        if (start.getTime() > end.getTime()) {
            throw new IllegalArgumentException("start is greater than end");
        }
    }

    public static void main(String[] args) {
        MaliciousDate start = new MaliciousDate();
        MaliciousDate end = new MaliciousDate();
        Period p = new Period(start, end);
        //...
    }

}

相关问题