为什么使用clone进行防御性复制会带来安全问题?

Efr*_*pez 7 java security defensive-programming

这些天我正在阅读Joshua Bloch的第二版Effective Java.在第39项中,他提到将一个可变对象的防御性副本作为参数传递是个好主意,比如在给定类Foo的构造函数中,如果这些对象稍后用于表示类Foo的状态.在同一个上下文中,他提到避免使用非final类的clone()方法,因为它可能返回一个设计用于执行恶意操作的不可信子类的实例.

这是我没有明确得到的.作为恶意子类的一个例子,他提到了一个类,它可以"在创建时记录对私有静态列表中每个实例的引用,并允许攻击者访问该列表".

我的疑惑:

  1. 他是否意味着这个恶意类实际上可以记录封装类的所有私有/受保护/包/公共实例的引用?

  2. 如果是这样,怎么可能呢?你能给我举个例子吗?

谢谢!

Tom*_*ine 8

与安全性一样,设置适用的上下文非常重要.我们对潜在恶意代码可以访问受攻击的受信任类的情况感兴趣.例如,在浏览器中,Java PlugIn可信库可以由不受信任的代码访问.曾经是RMI加载远程代码的情况,但它现在已经与默认安全策略一致.

可变参数的问题在于它们可以在检查有效性和使用它们之间进行更改.这称为检查/使用时间漏洞,TOCTOU(或TOC2TOU).在实践中,这可以是两种用途,而不是一种特别用于检查的用途.其他设计糟糕但看起来不可变但是可子类化(例如java.io.File)的类可以被子类化为可变的,作为它们在调用时执行任意代码的能力的一部分.

这里讨论的具体攻击场景clone是被覆盖以阻止复制尝试的地方.static在这种情况下,对a的引用是无关紧要的(它在finalizer攻击中很重要,但主要反映出攻击代码很少被设计为干净).

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

修改本书中的示例.

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;
} 
Run Code Online (Sandbox Code Playgroud)

然后攻击:

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!
Run Code Online (Sandbox Code Playgroud)

结论:使您的值类型具有强大的不可变性.有关Java编程语言的完全可靠的安全编码指南的更多信息.