最近我正在阅读 Joshua Bloch 的第二版 Effective Java。在第 39 项中,他提到制作作为参数传递的可变对象的防御副本是个好主意,比如在给定类 Foo 的构造函数中,如果这些对象稍后用于表示类 Foo 的状态。在同一上下文中,他提到要避免使用非最终类的 clone() 方法,因为它可能会返回旨在执行恶意操作的不受信任子类的实例。
这是我不太清楚的。作为恶意子类的示例,他提到了一个类,该类可以“在创建时在私有(private)静态列表中记录对每个实例的引用,并允许攻击者访问该列表”。
我的疑问:
他的意思是这个恶意类实际上可以记录封装类的所有private/protected/package/public实例的引用吗?
如果是这样,那怎么可能呢?你能给我举个例子吗?
谢谢!
最佳答案
作为典型的安全措施,列出适用的上下文非常重要。我们对潜在恶意代码可以访问受攻击的受信任类的情况感兴趣。例如,在浏览器中,不受信任的代码可以访问 Java PlugIn 受信任的库。以前是 RMI 加载远程代码的情况,但现在已经符合默认安全的策略。
可变参数的问题在于它们可以在检查有效性和使用之间更改。这被称为检查时间/使用时间漏洞,TOCTOU(或 TOC2TOU)。实际上,这可以是两种用途,而不是一种专门用于检查的用途。其他设计糟糕的类看起来不可变但可以子类化(例如 java.io.File
),可以被子类化为可变类,作为它们在调用时执行任意代码的能力的一部分。
这里讨论的特定攻击场景是覆盖 clone
以阻止复制尝试。在这种情况下,对 static
的引用是无关紧要的(它在 finalizer
攻击中很重要,但主要反射(reflect)了攻击代码很少被设计成干净的)。
class MaliciousDate {
private final List<MaliciousDate> dates;
public MaliciousDate(List<MaliciousDate> dates) {
this.dates = dates;
}
@Override public MaliciousDate clone() {
MalicousDate other = (MalicousDate)super.clone(); // Or new MalicousDate
synchronized (dates) {
dates.add(other);
}
return other; // Or return this;
}
}
修改书上的例子。
public Period(Date start, Date end) {
// Failing defensive copy.
start = (Date)start.clone();
end = (Date)end .clone();
if (start.compareTo(end) > 0)
throw new IllegalArgumentExcpetion();
this.start = start;
this.end = end;
}
然后攻击:
List<MaliciousDate> dates = new ArrayList<>()
Date start = new MaliciousDate(dates);
Date end = new MaliciousDate(dates);
Period p = new Period(start, end);
dates.get(1).setYear(78); // Modifies internals of p!
结论:使您的值类型稳健不可变。更多信息在非常棒的Secure Coding Guidelines for the Java Programming Language .
关于java - 为什么使用克隆进行防御性复制会带来安全问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21897622/