免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1622 | 回复: 1
打印 上一主题 下一主题

可变hashcode的隐患和序列化安全 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-02-24 11:29 |只看该作者 |倒序浏览
可变hashcode的隐患



    为识别对象,JDK为每个Object类都定义了一个hashcode,Object的类的hashcode是根据对象的内存地址做hash算法得出来的,String类则自己重写了hashcode()方法,是根据字符串的每个字符做算法累加起来的,Integer在直接返回value的值。

    而很多时候,对于应用系统的一些类(Java Bean),是要根据属性来计算hashcode而非内存地址,就像String类。所以会去覆盖Object的equals方法。其实重写就重写了,但是出于技术上的要求,JDK的一些数据结构Collection作为一个数据的容器,它需要唯一定位某个对象,而判定对象是否相等的标准是:1,hashcode是否相等;2,equals函数是否返回true。所以如果这些Java Bean用到这些Collection的话重写equals就必须同时重写hashcode了。

    但这不是重点,重点是另外一个问题:可变的hashcode带来的BUG。



大师Ted给出了例子,如下:
  1. import java.util.*;

  2. public class Person
  3.     implements Iterable<Person>
  4. {
  5.     public Person(String fn, String ln, int a, Person... kids)
  6.     {
  7.         this.firstName = fn; this.lastName = ln; this.age = a;
  8.         for (Person kid : kids)
  9.             children.add(kid);
  10.     }
  11.    
  12.     // ...
  13.    
  14.     public void setFirstName(String value) { this.firstName = value; }
  15.     public void setLastName(String value) { this.lastName = value; }
  16.     public void setAge(int value) { this.age = value; }
  17.    
  18.     public int hashCode() {
  19.         return firstName.hashCode() & lastName.hashCode() & age;
  20.     }

  21.     // ...

  22.     private String firstName;
  23.     private String lastName;
  24.     private int age;
  25.     private List<Person> children = new ArrayList<Person>();
  26. }


  27. // MissingHash.java
  28. import java.util.*;

  29. public class MissingHash
  30. {
  31.     public static void main(String[] args)
  32.     {
  33.         Person p1 = new Person("Ted", "Neward", 39);
  34.         Person p2 = new Person("Charlotte", "Neward", 38);
  35.         System.out.println(p1.hashCode());
  36.         
  37.         Map<Person, Person> map = new HashMap<Person, Person>();
  38.         map.put(p1, p2);
  39.         
  40.         p1.setLastName("Finkelstein");
  41.         System.out.println(p1.hashCode());
  42.         
  43.         System.out.println(map.get(p1));
  44.     }
  45. }
复制代码
可以看到这个Person重写了hashcode,是属性的hashcode做与运算得出的结果。但是注意到,当p1对象作为key放入HashMap后,随后改变了lastName的属性,导致p1对象的hashcode发生了改变,从而System.out.println(map.get(p1));直接输出null。

    所以可以看到,在重写hashcode的时候要特别注意可变的hashcode的影响,为了消除这种隐患,最好给属性加上final的限制。





序列化安全问题

    java中序列化对象可以用户磁盘存储和网络传输对象等,但如果不经过任何处理则这些流都是明文的,具有安全隐患,我们通过模糊化处理或使用SealedObject加密等方法来保证安全,如下:



1,模糊化处理,如下:

    JAVA对于序列化、反序列化的类是利用ObjectOutputStream、ObjectIntputStream实现,如果需要序列化的类没有writeObject和readObject的方法话,则会调用默认的序列化方式,所以只要重写这两个方法就可以实现自定义的方式来增加安全性等特殊处理(JDK应该是考虑到了安全等问题才给与扩展),如下:
  1. package com.tedneward;

  2. public class Person
  3.     implements java.io.Serializable
  4. {
  5.     public Person(String fn, String ln, int a)
  6.     {
  7.         this.firstName = fn; this.lastName = ln; this.age = a;
  8.     }
  9.    
  10.     public String getFirstName() { return firstName; }
  11.     public String getLastName() { return lastName; }
  12.     public int getAge() { return age; }
  13.     public Person getSpouse() { return spouse; }
  14.    
  15.     public void setFirstName(String value) { firstName = value; }
  16.     public void setLastName(String value) { lastName = value; }
  17.     public void setAge(int value) { age = value; }
  18.     public void setSpouse(Person value) { spouse = value; }
  19.    
  20.     private void writeObject(java.io.ObjectOutputStream stream)
  21.         throws java.io.IOException
  22.     {
  23.         // "Encrypt"/obscure the sensitive data
  24.         age = age << 2;
  25.         stream.defaultWriteObject();
  26.     }
  27.    
  28.     private void readObject(java.io.ObjectInputStream stream)
  29.         throws java.io.IOException, ClassNotFoundException
  30.     {
  31.         stream.defaultReadObject();

  32.         // "Decrypt"/de-obscure the sensitive data
  33.         age = age >> 2;
  34.     }
  35.    
  36.    
  37.     private String firstName;
  38.     private String lastName;
  39.     private int age;
  40.     private Person spouse;
  41. }
复制代码
2,使用SealedObject对对象进行加密,然后序列化,如下:
  1. import java.io.Serializable;
  2. import java.security.Security;

  3. import javax.crypto.Cipher;
  4. import javax.crypto.KeyGenerator;
  5. import javax.crypto.SealedObject;
  6. import javax.crypto.SecretKey;

  7. public class MainClass {
  8.   public static void main(String args[]) throws Exception {
  9.     Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
  10.     SecretKey secretKey;
  11.     Cipher encrypter, decrypter;

  12.     secretKey = KeyGenerator.getInstance("DES").generateKey();// des加密

  13.     encrypter = Cipher.getInstance("DES");
  14.     encrypter.init(Cipher.ENCRYPT_MODE, secretKey);

  15.     decrypter = Cipher.getInstance("DES");
  16.     decrypter.init(Cipher.DECRYPT_MODE, secretKey);

  17.     MyClass cust, unsealed;
  18.     SealedObject sealed;

  19.     cust = new MyClass();
  20.     cust.name = "Paul";
  21.     cust.password = "password";

  22.     // Seal it, storing it in a SealedObject
  23.     sealed = (new SealedObject(cust, encrypter));
  24.    
  25.     FileOutputStream fos = new FileOutputStream(args[0]);
  26.     ObjectOutputStream oos = new ObjectOutputStream(fos);
  27.     oos.writeObject(sealed);
  28.     oos.close();

  29.     // Try unsealing it
  30.     String algorithmName = sealed.getAlgorithm();
  31.     System.out.println(algorithmName);
  32.     unsealed = (MyClass) sealed.getObject(decrypter);

  33.     System.out.println("NAME: " + unsealed.name);
  34.     System.out.println("PASSWORD: " + unsealed.password);

  35.   }
  36. }

  37. class MyClass implements Serializable {
  38.   public String name;

  39.   public String password;
  40. }
复制代码

论坛徽章:
0
2 [报告]
发表于 2011-02-24 15:22 |只看该作者
学习下
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP