Java中能否使用StringBuffer替代char[]数组存储密码?
结论
不推荐使用StringBuffer替代char[]存储密码,char[]仍然是Java中存储明文密码的最优选择。
核心原因
行业内不推荐用String存密码的核心逻辑非常明确:密码是高敏感信息,要尽可能缩短它在内存中以明文形式存在的时间,用完就可以主动、彻底擦除内存痕迹,避免内存dump、核心转储、日志误打印等场景下的信息泄露。而StringBuffer虽然是可变类,依然满足不了这个安全要求,问题主要有三点:
- 内部存储存在不可控的明文残留
StringBuffer内部靠动态扩容的char[]存储内容,一旦写入的内容长度超过当前数组容量,就会新建容量更大的新数组,把旧内容拷贝过去。整个过程产生的旧数组副本对上层开发者完全不可见,你根本没有权限主动擦除旧数组里留存的密码明文,只能等GC不定时回收,这段时间窗口里敏感数据完全可能被恶意工具窃取。如果是手动创建维护的char[],数组长度完全由开发者控制,使用完后立刻遍历数组把每个元素覆写为\0或者随机值,就能马上彻底擦除所有明文痕迹,不会留下任何隐藏副本。 - 自带的线程安全特性完全冗余
StringBuffer所有对外暴露的操作方法都加了synchronized内置锁来保证多线程安全,但密码存储本身是短生命周期的单线程临时操作,根本不需要跨线程同步的安全保证,这个设计只会平白增加性能开销,没有任何实际收益。 - 意外泄露的风险更高
如果不小心调用了StringBuffer的toString()方法,会直接生成一个不可变的String对象存入堆内存,这个生成的String你无法主动擦除,直接就退化成了String存密码的高风险场景。另外StringBuffer是非常通用的字符串封装类,很多日志框架、调试工具、序列化组件会默认读取它的内容做输出,很容易在开发者没感知的情况下把密码泄露出去;反观原生char[]直接打印只会输出对象的内存地址,不会直接暴露数组里存储的具体内容,可控性要高很多。
补充说明:哪怕是去掉了线程安全锁、性能更好的
StringBuilder,也存在和StringBuffer一样的动态扩容残留、意外生成不可变String的问题,同样不适合用来存储密码。
内容的提问来源于stack exchange,提问作者Aman Kumar
相关产品推荐
相关产品推荐

