Java:为什么不应该将 clone() 用于防御性复制?

Java: Why shouldn't clone() be used for defensive copying?

在 Effective Java(第 7 章)中,它说

Note also that we did not use Date’s clone method to make the defensive copies. Because Date is nonfinal, the clone method is not guaranteed to return an object whose class is java.util.Date: it could return an instance of an untrusted subclass specifically designed for malicious mischief. Such a subclass could, for example, record a reference to each instance in a private static list at the time of its creation and allow the attacker to access this list. This would give the attacker free reign over all instances. To prevent this sort of attack, do not use the clone method to make a defensive copy of a parameter whose type is subclassable by untrusted parties.

我不太明白它的解释。为什么 clone() 不是 return Date 对象?实例怎么可能是不受信任的子类?

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

考虑这段代码:

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();
    }
}

因为 copyOfMaliciousDateDate 类型,你可以调用 clone() 并且它会 return 一个 Date 对象,但是调用 clone on copyOfMaliciousDate 执行写在MaliciousDate class' clone() 中的代码,因为 instance 存储在 copyOfMaliciousDateMaliciousDate.

我没有读过你引用的那本书,但那段话的理由很差,也没有提供任何针对任何攻击的保护措施。

该引文提到,能够将代码加载到您的程序中的攻击者可能会使用恶意方法提交 Date subclass,例如返回 subclass Date 来自 clone.

但这只是具有加载代码能力的攻击者造成危害的次要方式。他们还可以:

  • 使用反射获得对几乎所有标记为私有的内容的读写访问权限,
  • 与 class 加载程序混淆以加载他们自己的 classes,
  • 版本
  • 调用 System.exit() 停止您的程序,然后
  • 执行您的程序可以执行的任何操作,例如生成其他程序或访问文件。

如果攻击者是您进程中的 运行 代码,那么游戏就结束了,您的进程就会受到威胁,这个愚蠢的小守卫不会提供帮助。

也许您认为克隆从设计的角度来看很糟糕,这很好,但请不要假装不使用它会保护您免受某些安全威胁,因为它不会。