本节书摘来异步社区《Java编码指南:编写安全可靠程序的75条建议》一书中的第1章,第1.3节,作者:【美】Fred Long(弗雷德•朗), Dhruv Mohindra(德鲁•莫欣达), Robert C.Seacord(罗伯特 C.西科德), Dean F.Sutherland(迪恩 F.萨瑟兰), David Svoboda(大卫•斯沃博达),更多章节内容可以访问云栖社区“异步社区”公众号查看。
字段的不变性可以防止其被意外修改以及恶意篡改,因此在接受输入或返回值时,防御性复制不可变字段是不必要的。然而,部分敏感类由于某些原因必须要被改变。幸运的是,可以通过不可修改的包装器,将可变类的只读访问权限授予不可信代码。例如,Collection(集合)类包括一组包装器,允许客户端观察一个不可修改的集合对象视图。
在下面的违规代码示例中,Mutable这个类允许内部数组对象被修改:
class Mutable { private int[] array = new int[10]; public int[] getArray() { return array; } public void setArray(int[] i) { array = i; } } // ...``` private Mutable mutable = new Mutable(); public Mutable getMutable() {return mutable;} 不可信的调用程序能调用setArray()方法,这违反了对象的不变性属性。调用getArray()方法还允许修改该类的私有内部状态。此外,这个类还违反了《The CERT® Oracle® Secure Coding Standard for Java™》[Long 2012]的“OBJ05-J. Defensively copy private mutable class members before returning their references”。 ####违规代码示例 在下面的违规代码示例中,MutableProtector继承了Mutable类。class MutableProtector extends Mutable { @Override public int[] getArray() { return super.getArray().clone(); }}// ...`private Mutable mutable = new MutableProtector();// May be safely invoked by untrusted caller having read abilitypublic Mutable getMutable() {return mutable;}在这个类中,调用getArray()方法不允许修改该类的私有内部状态,符合“OBJ05-J. Defensively copy private mutable class members before returning their references”[Long 2012]。然而,不可信的调用程序能调用setArray()方法修改Mutable对象。
一般来说,通过对核心接口定义的所有方法(包括赋值方法),提供合适的包装器,可以将敏感类转化为安全视图(safe-view)对象。赋值方法的包装器必须抛出UnsupportedOperationException异常,这样调用者就不太可能做出影响属性不变性的操作。
在下面的解决方案中,setArray()方法覆盖了Mutable.setArray()方法,防止了对Mutable对象的改变。
class MutableProtector extends Mutable { @Override public int[] getArray() { return super.getArray().clone(); } @Override public void setArray(int[] i) { throw new UnsupportedOperationException(); } } // ...``` private Mutable mutable = new MutableProtector(); // May be safely invoked by untrusted caller having read ability public Mutable getMutable() {return mutable; } MutableProtector类覆盖了Mutable类的getArray()方法,该方法克隆了Mutable类的原始数组。因此,尽管调用代码能够得到Mutable对象中的数组字段的数据,但原始数组是保持不变的,并且调用代码不能访问到原始数组。当调用者试图调用MutableProtector对象的setArray()方法时,覆盖后的setArray()方法会抛出一个异常。这样保证了MutableProtector对象可以被传递给不可信代码,因为该对象只允许读操作。 ####适用性