下面是基于Gong [1999]、McGraw [1999]和Sun的指南的若干关键要点: public final void clone() throws java.lang.CloneNotSupportedException { throw new java.lang.CloneNotSupportedException(); } 如果确实需要使类可被复制,那么可以采用几个保护措施来防止攻击者重新定义复制方法。如果是定义自己的复制方法,只需要使它是确定的。如果不是定义自己的复制方法,至少可以通过增加如下内容来防止复制方法被恶意地重载: public final void clone() throws java.lang.CloneNotSupportedException { super.clone(); } 应该使类不可序列化。系列化运行攻击者看到对象的内部状态,甚至私有部分。要防止这一点,需要在类里增加如下方法: private final void writeObject(ObjectOutputStream out) throws java.io.IOException { throw new java.io.IOException("Object cannot be serialized"); } 甚至在序列化没问题的情况下,也应该对包含直接处理系统资源的域和包含与地址空间有关信息的域使用临时关键字。否则,解除类的序列化就会允许不适当的访问。可能还需要把敏感信息标识为临时的。 如果对类定义了自己的序列化方法,就不应该把内部数组传递给需要数组的DataInput/DataOuput方法。其理由在于:所有的DataInput/DataOuput方法都可以被重载。如果某个可序列化的类向某个DataOutput(write(byte [] b))方法直接传递了一个私有数组,那么攻击者就可以构建子类ObjectOutputStream并重载write(byte [] b)方法,从而可以访问并修改那个私有数组。注意,缺省的序列化并没有把私有字节数组域暴露给DataInput/DataOutput字节数组方法。 应该使类不可被解除序列化。即使类不可被序列化,它依然可以被解除序列化。攻击者可以构建一个字节序列,使它碰巧是被解除序列化的某个类实例,而且具有攻击者选定的值。换句化话说,解除序列化是一种公共的构建函数,允许攻击者选择对象的状态 -- 显然是一个危险的操作! 要防止这一点,需要在类里增加如下方法: private final void readObject(ObjectInputStream in) throws java.io.IOException { throw new java.io.IOException("Class cannot be deserialized"); } 不要通过名称来比较类。毕竟攻击者可以用相同的名称定义类,而且一不小心就会授予这些类不恰当的权限。因此,下面是一个判断某个对象是否含有某个给定类的错误方法的例子: if (obj.getClass().getName().equals("Foo")) { 如果要判断两个对象是否含有完全相同的类,不要对双方使用getClass()并使用“==”操作符进行比较,而应该使用如下形式: if (a.getClass() == b.getClass()) { 如果确实需要判断某个对象是否含有某个给定类名,需要严格按照规范并确保使用当前名称空间(当前类的ClassLoader所在名称空间)。因此,应该使用如下形式: if (obj.getClass() == this.getClassLoader().loadClass("Foo")) { 本原则来自McGraw和Felten,而且确实是个好原则。要补充的是,尽可能地避免比较类值通常是个好注意。通常最好是尽力设计类的方法和接口,从而完全不必要做这些事。尽管如此,实际上无法完全做到,所以知道这些技巧还是很重要的。 不要把秘密(密钥、密码或算法)存储在代码或数据里。有恶意的JVM可以迅速看到这一数据。打乱代码并不能在认真的攻击者面前实际隐藏代码。 |
温馨提示:喜欢本站的话,请收藏一下本站!