为什么要加密内存中的数据?

信息安全 加密 记忆 保密 虚拟内存
2021-08-16 02:15:00

我看到 KeePass 不仅加密它的密码数据库文件,它还可以加密它保存在内存中的密码。这只是一个例子。我正在考虑一个处理敏感/个人数据的新项目,现在我问自己是否也应该加密内存中的数据。该项目将使用 Java SE 和一个附加的 android 应用程序来实现。在这种特殊情况下,不会有数据存储在云或服务器上。Java SE 桌面应用程序将通过电缆连接导入来自 android 的数据。

但为什么这是必要的呢?现代操作系统是否不使用虚拟内存管理,以便用户空间/用户模式进程无法访问其他进程内存?

如果存在使外部内存访问成为可能的操作系统漏洞,这是否只是另一道防线?在这种情况下,我认为窃取数据文件并使用键盘记录器来捕获用户输入的密码要比通过内存访问窃取数据要容易得多。

4个回答

在一个完美的世界里,你是对的:在 RAM 中保持数据加密应该没有意义。操作系统应该在进程之间保持严格的分离,在将 RAM 重新分配给另一个进程时清除 RAM,并且,如果攻击模型允许攻击者在之后窃取设备并进行一些硬盘分析,则加密交换(或根本不使用交换,这对于具有基于闪存的存储的设备可能更有意义)。

在现实世界中,操作系统和系统管理是人类的努力,因此本质上是不完美的,您可能需要添加一些保护措施,包括在 RAM 中加密数据并指示操作系统不要在交换空间中写入数据(在 Unix-像系统一样,这是通过mlock())完成的尽管如此,这种在 RAM 中加密的东西更像是一种心理仪式,而不是真正的防御。它的主要优点是让开发人员感觉更好。

无论如何,不​​要费心在Java中这样做。垃圾收集器将复制在RAM透明的对象(这是最有效的GC算法的一部分,你不能阻止它),这样您的应用程序没有任何级别的加密也保证了按键的没有明确的版本,在RAM中随时存在。

现代操作系统使用虚拟内存管理,因此默认情况下用户空间/用户模式进程无法直接访问其他进程的内存。

但是在 Windows 中(不知道这是否也适用于 Linux)有一些接口允许标准用户访问使用相同凭据运行的其他进程的进程内存。

有很多程序使用这个接口。一个非常简单的例子是HxD - 一个免费的十六进制编辑器,允许查看甚至编辑其他进程的进程内存。

此外,还有一些已知的攻击,例如 Spectr 或RowHammer,它们可以在没有操作系统帮助的情况下访问或损坏其他进程的内存。

内存可以通过以下方式对其他进程可见:

  1. 一旦使用它的原始进程将其返回给操作系统,就可以使用它。内存未清除,后续进程可以执行malloc()并检索属于先前运行的进程的信息(注意Linux 设备驱动程序的第 8 章- 特别是第一页上的脚注)
  2. 操作系统将页面换出到磁盘。然后这些可以用于监视磁盘/存储的进程。

因此,未加密的内存可能对其他进程可见。

是一个非常有趣的链接,请注意以下引用:

然而,这种波动性很大程度上取决于所讨论的计算机 - 因为当计算机不做任何事情时,匿名内存可以持续很长时间。例如,在某些计算机中,密码和其他预先计算的数据在输入或加载到内存后数天后很容易恢复

您必须考虑一些特定于 Java 世界的问题。如果出现技术问题,对 JVM 进行堆转储是一种正常做法。我正在处理一个甚至具有专用 UI​​ 的应用程序。这可能非常有价值,例如用于查找内存泄漏。当然 - 是的 - 敏感信息进入转储。因此,如果有人在应用程序中中断(例如,使用 SQL 注入绕过登录 :) )... :X。或者如果管理用户是一个好奇的人......:X

我知道这不应该发生在“理想世界”中。

您还可以配置 JVM 以对内存不足异常进行转储。从 Java 1.4 开始,这可能看起来像这样:

-XX:-HeapDumpOnOutOfMemoryError  -XX:HeapDumpPath=<path to dump file>

攻击者可能会发现导致应用程序因内存不足而崩溃的漏洞,并可能会发现另一个漏洞(例如文件路径遍历错误)来窃取您的转储!

您应该考虑的另一件事是某些敏感数据必须以某种方式进入您的应用程序。所以在某些时候你会记住它。您对此几乎无能为力。但是您可以做的是更快地清洁它。例如获取用户密码。如果将其存储在 String 实例中,那么您无法控制何时将其作为垃圾收集。因此,通常好的 API 会在字符数组 (char[]) 中处理此类数据,这些数据可以在使用后归零。