8

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

这是我不清楚的。作为恶意子类的一个例子,他提到了一个类,该类可以“在创建私有静态列表时记录对每个实例的引用,并允许攻击者访问该列表”。

我的疑惑:

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

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

谢谢!

4

2 回答 2

9

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

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

这里讨论的特定攻击场景是clone被覆盖以阻止复制尝试的地方。在这种情况下,对 a 的引用static是无关紧要的(它在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;
    }
}

修改书中的示例。

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!

结论:让你的值类型健壮地不可变。更多信息,请参阅非常棒的 Java 编程语言安全编码指南

于 2014-02-20T08:14:39.173 回答
1

恶意子类的方法可以通过getDeclaredFieldsclone方法访问其超类的私有成员——这将返回所有超类的字段,甚至是那些被声明为私有的字段。

我相信这本书所指的是该方法还可以存储通过该方法clone实例化的所有实例的列表。clone

class MaliciousClass extends LegitimateClass {
  public static ArrayList privateData = new ArrayList()
  public static ArrayList clonedInstances = new ArrayList();
  protected Object clone() {
    Fields[] fields = this.getSuperclass().getDeclaredFields();
    for(Field field: Fields) {
      privateData.add(field.get(this));
    }
    Object clonedObject = // perform clone, returning an instance of MaliciousClass
    clonedInstances.add(clonedObject);
    return clonedObject;
  }
}
于 2014-02-20T03:55:23.810 回答