内存中的敏感信息使用完毕后应立即清0
为什么推荐优先使用char[]/byte[]存储敏感信息
- 字符串变量是不可变的。一旦分配了字符串变量,就无法更改或删除其值。因此,这些字符串可能在可能在多个位置的存储器中保持不变,持续一段时间,直到垃圾收集器正好将其移除,且由于字符串是不可变的,所以没有任何方式可以修改字符串的值,因为每次修改都将产生新的字符串。敏感数据(如密码)将作为明文在内存中暴露,无法控制其生命周期因为任何能够访问内存(memory dump内存转储)的人都能清晰的看到文本中的密码,这也是为什么你应该总是使用加密的形式而不是明文来保存密码。因为String使用常量池存储字符串,即使jdk7之后常量池移到了堆,也要在触发gc之后才能彻底清除掉内存中的敏感信息。
- Java官方文档《Java加密体系结构指南》 (https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#PBEEx) 建议:在对象java.lang.String 中收集和存储密码看似符合逻辑,但是需要注意的是,字符串对象是不可变的,没有任何方法可以改变(重写)或清空内容。这一特性使得字符串对象不适合存储安全敏感信息,比如用户密码。你应该使用字符数组收集和存储安全敏感信息。
- 建议使用专用类存储明文的敏感信息,或者,将明文敏感信息临时存储在可变数据类型(如:char[]/byte[])中,且用完后立即擦除。如果没有及时清空而由GC来清除的话,暴露窗口大约是秒这个数量级,如果能够在计算HASH后立即清除,暴露窗口大约是微秒数量级。如此简单的设计就可以降低如此多的被攻击概率,性价比是非常高的。
- 其他问题:通过reflection机制可以查看String的内部的内存成员,从而可以直接修改其中的数据区。但是这样的做法会有问题,内部化的String为了提高HASH速度,节省空间,值相同的字符串通常只有一个实例。
你自己的char[],修改它是没有副作用的。但是String里的char[],很可能是多个String所共享的,你改掉它就会殃及别的String。举个例子,有一个密码是"Password",而你密码框提示密码输入的文字也是"Password",改掉第一个"Password"会把后面那个也改掉。