上下文
我最近有一个加密讲座,我们讨论了关于内存中关键元素的持久性。通常,C / C ++库Libsodium建议清除任何包含敏感信息的缓冲区,例如秘密(ref)。我知道GuardedString
是由一个字节数组支持的,文档建议在不再使用存储的密码后调用方法dispose
,使用Arrays.fill
填充字节数组。
题
JVM是否保证在覆盖时字节数组的值消失,或者在某些条件下原始值是否保留在内存中?例如,在Java字符串池中保留未使用/未引用的String
s,直到触发垃圾收集。是否有类似的缓存或其他类型的机制,如字节数组,可以破坏应该从GuardedString
处置的秘密? JVM规范中的任何引用?
非常感谢 !
在Java中,通常会使用char[]
数组而不是String
,因为这允许手动将数组中的数据归零。
然而,即使这样,根据this answer,数据也可能没有完全取消:
正如评论中所指出的那样,垃圾收集器移动的数组可能会将数据的杂散副本留在内存中。我相信这是特定于实现的 - 垃圾收集器可以清除所有内存,以避免这种情况。即使它确实存在,仍然有时间char []包含实际字符作为攻击窗口。
如果编译器决定优化memset
,则在C / C ++中存在类似的问题。根据11.4. Specially Protect Secrets (Passwords and Keys) in User Memory:
Andy Polyakov(2002年11月7日)发布的Bugtraq帖子报道,C / C ++编译器gcc版本3或更高版本,SGI MIPSpro和Microsoft编译器消除了对memset的简单内联调用,旨在覆盖机密。这是C和C ++标准所允许的。其他C / C ++编译器(例如gcc小于版本3)在所有优化级别保留了对memset的内联调用,表明该问题是特定于编译器的。简单地声明目标数据是易失性的并没有帮助所有编译器; MIPSpro和Microsoft编译器都忽略了简单的“挥发”。简单地“触摸”秘密数据的第一个字节也无济于事;他发现MIPSpro和GCC> = 3只巧妙地使第一个字节无效并保持其余部分完整(这实际上非常聪明 - 问题在于编译器的聪明性正在干扰我们的目标)。