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

Java 为什么使用克隆进行防御性复制会带来安全问题?,java,security,defensive-programming,Java,Security,Defensive Programming,这些天我正在阅读Joshua Bloch的《有效Java》第二版。在第39项中,他提到,如果这些对象后来被用来表示类Foo的状态,那么最好制作作为参数传递的可变对象的防御性副本,比如在给定类Foo的构造函数中。在相同的上下文中,他提到要避免使用非final类的clone()方法,因为它可能返回一个不受信任的子类的实例,该子类设计用于执行恶意操作 这是我不清楚的。作为恶意子类的一个示例,他提到了一个类,该类可以“在创建私有静态列表时记录对每个实例的引用,并允许攻击者访问该列表” 我的疑问是: 他是

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

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

我的疑问是:

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

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


  • 谢谢

    恶意子类的
    clone
    方法可以通过该方法访问其超类的私有成员-这将返回超类的所有字段,甚至是声明为私有的字段

    我相信这本书所指的是
    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;
      }
    }
    

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

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

    此处讨论的特定攻击场景是覆盖
    克隆
    ,以阻止复制尝试。对
    静态
    的引用在此上下文中是不相关的(这在
    终结器
    攻击中很重要,但主要反映出攻击代码很少被设计为干净的)

    然后攻击:

    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!
    
    List dates=new ArrayList()
    开始日期=新恶意日期(日期);
    日期结束=新的恶意日期(日期);
    期间p=新期间(开始、结束);
    日期。获取(1)。设置年份(78);//修改p的内部结构!
    

    结论:使您的值类型稳固不变。更多的信息,在完全可怕的

    不管怎样,它肯定可以做到这一点,而无需克隆?如果安装了SecurityManager,getDeclaredFields将抛出SecurityException(除非明确允许)。是的,反射或多或少受限于您可以执行的操作。当受信任的代码出于某种狡猾的目的使用反射时,就会引入漏洞。正是Java编程语言本身的基本特性(以及它的使用方式)导致了这里的问题。
    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!