14

我正在开发一个 C++ 应用程序,它将一些用户密钥保存在 RAM 中。这个密钥是高度敏感的,我必须尽量减少对它们进行任何类型攻击的风险。
我正在使用字符数组来存储这些键,我已经阅读了一些关于在 CPU 寄存器甚至 CPU 缓存中存储变量的内容(即使用 C++register关键字),但似乎没有一种保证方法可以强制应用程序存储一些它是 RAM 之外的变量(我的意思是在 CPU 寄存器或缓存中)。
任何人都可以提出一个好的方法来做到这一点或建议任何其他解决方案来将这些密钥安全地保存在 RAM 中(我正在寻找一个独立于操作系统的解决方案)?

4

8 回答 8

17

你的意图可能是崇高的,但它们也被误导了。简短的回答是,在通用系统(即商品处理器/主板和通用O/S)上确实没有办法做你想做的事。即使您可以以某种方式强制将内容仅存储在 CPU 上,它仍然无济于事。这只是一个小麻烦。

更一般地,对于保护内存的问题,有特定于操作系统的解决方案表明不应将块内存写入页面文件,例如VirtualLockWindows 上的函数。如果您正在加密并在该内存中保存敏感数据,那么这些都是值得使用的。

最后一件事:我要指出让我担心的是您对register关键字及其安全含义有根本的误解;请记住,这是一个提示,它不会 - 事实上,它不能- 强制将任何内容实际存储在寄存器或其他任何地方。

现在,这本身并不是什么大问题,但它是一个问题,因为它表明您并没有真正掌握安全工程或风险分析,如果您正在设计或实施,这是一个大问题真实世界的加密解决方案。坦率地说,您的帖子表明(至少对我而言)您还没有准备好构建或实施这样的系统。

于 2013-05-11T18:53:03.803 回答
11

您无法消除风险,但可以减轻风险。

创建一个静态内存区域,这将是您存储明文密钥的唯一位置。并创建一个随机数据缓冲区,您将使用它来异或任何未存储在这个静态缓冲区中的键。

每当您将密钥从密钥文件或其他内容读入内存时,您只需将其直接读入这个静态缓冲区,与随机数据进行异或并将其复制到您需要的任何地方,然后立即用零清除缓冲区。

您可以通过比较它们的掩码版本来比较任何两个键。您甚至可以比较掩码键的哈希值。

如果您需要对明文密钥进行操作 - 例如生成哈希或验证他们的密钥以某种方式将屏蔽的异或密钥加载到这个静态缓冲区中,将其异或返回到明文并使用它。然后将零写回该缓冲区。

取消屏蔽、操作和重新屏蔽的操作应该是快速的。不要让缓冲区长时间处于未屏蔽状态。

如果有人要尝试冷启动攻击,拔下硬件插头并检查内存芯片,那么只有一个缓冲区可以保存明文密钥,并且在冷启动攻击的特定时刻,缓冲区很有可能将是空的。

在对密钥进行操作时,您甚至可以在需要验证密钥之前一次只取消屏蔽密钥的一个字,这样完整的密钥就不会存储在该缓冲区中。

@update:我只是想在下面的评论中解决一些批评:

“通过默默无闻的安全”这一短语通常被误解。在安全算法的形式分析中,“模糊性”或隐藏非密码安全数据的方法不会增加密码算法的形式安全性。在这种情况下确实如此。鉴于密钥存储在用户机器上,并且必须由该机器上的该程序使用,因此无法使该机器上的密钥加密安全。无论您使用什么过程来隐藏或锁定数据,程序都必须在某些时候使用它,而坚定的黑客可以在代码中设置断点并观察程序何时使用数据。但是这个线程中没有任何建议可以消除这种风险。

有人建议 OP 找到一种方法来使用带有锁定内存芯片的特殊硬件或锁定芯片的某种操作系统方法。这在密码学上不再安全。最终,如果您对机器有物理访问权限,那么一个足够坚定的黑客可以在内存总线上使用逻辑分析仪并恢复任何数据。此外,OP 已经声明目标系统没有这样的专用硬件。

但这并不意味着您无法采取任何措施来降低风险。使用最简单的访问密钥——密码。如果您可以物理访问机器,则可以放入键盘记录器,或获取正在运行的程序的内存转储等。因此,从形式上讲,密码并不比以明文形式写在粘​​在键盘上的便签上更安全。然而,每个人都知道在便签纸上保留密码是一个坏主意,对于程序以明文形式向用户回显密码是不好的做法。当然,实际上,这大大降低了攻击者的门槛。然而,正式的带有密码的便签也同样安全。

我上面提出的建议具有真正的安全优势。除了安全密钥的“异或”屏蔽之外,任何细节都不重要。并且有一些方法可以使这个过程变得更好一些。异或密钥将限制程序员必须考虑作为攻击向量的位置数量。一旦密钥被异或,您可以在整个程序中拥有不同的密钥,您可以复制它们,将它们写入文件,通过网络发送它们等。除非攻击者拥有异或缓冲区,否则这些事情都不会危及您的程序。所以有一个你必须担心的单一缓冲区。然后,您可以放松系统中的所有其他缓冲区。(并且您可以 mlock 或 VirtualLock 那个缓冲区)

清除异或缓冲区后,您就可以永久且安全地消除攻击者从程序的内存转储中恢复任何密钥的可能性。您在位置数量和可以恢复密钥的时间方面限制了您的曝光。并且您正在建立一个系统,该系统允许您轻松使用密钥,而无需担心在对包含密钥的对象进行每次操作期间可能会以简单的方式恢复密钥。

因此,您可以想象一个系统,其中键对异或缓冲区进行计数,当不再需要所有键时,您将归零并删除异或缓冲区,并且所有键都变得无效且无法访问,而无需跟踪它们并担心是否存在内存页面被换出并仍然保存明文密钥。

您也不必真正保留随机数据的缓冲区。例如,您可以使用加密安全的随机数生成器,并根据需要使用单个随机种子生成异或缓冲区。攻击者恢复密钥的唯一方法是访问单个生成器种子。

您还可以根据需要在堆栈上分配明文缓冲区,并在完成后将其清零,这样堆栈极不可能留在芯片缓存中。如果从未解码完整的密钥,而是根据需要一次解码一个单词,即使访问堆栈缓冲区也不会显示密钥。

于 2013-05-11T20:14:41.313 回答
5

没有独立于平台的解决方案。您要解决的所有威胁都是特定于平台的,因此解决方案也是如此。没有法律要求每个 CPU 都有寄存器。没有法律要求 CPU 具有缓存。其他程序访问您程序的 RAM 的能力,实际上其他程序的存在,都是平台细节。

您可以创建一些函数,例如“分配安全内存”(默认调用malloc)和“空闲安全内存”(默认调用memsetthen free),然后使用它们。您可能需要在需要其他操作的平台上执行其他操作(例如锁定内存以防止您的密钥在交换中结束)。

于 2013-05-11T18:48:56.757 回答
2

除了上面非常好的评论之外,您还必须考虑,即使成功地将密钥存储在寄存器中,当中断到来和/或另一个任务时,寄存器内容很可能会存储在内存中在机器上运行。当然,可以物理访问机器的人可以运行调试器并检查寄存器。如果密钥足够重要以至于有人会在这样的设备上花费数千美元,那么调试器可能是“电路仿真器”——这意味着目标系统上根本没有软件。

另一个问题当然是这有多重要。密钥来自哪里?有人输入吗?如果不是,并且存储在其他地方(在代码中,服务器上等),那么它们将在某个时候存储在内存中,即使您在实际使用密钥时成功地将它们排除在内存之外。如果有人正在输入它们,那么某人以某种方式或另一种方式迫使知道密钥的人透露密钥是否存在安全风险?

于 2013-05-11T19:53:06.173 回答
2

正如其他人所说,在通用计算机上没有安全的方法可以做到这一点。另一种方法是使用硬件安全模块(HSM)。

这些提供:

  • 与普通 PC/服务器相比,对密钥的物理保护更强(防止直接访问 RAM);
  • 更大的逻辑保护,因为它们不是通用的 - 机器上没有运行其他软件,因此没有其他进程/用户可以访问 RAM。

您可以使用 HSM 的 API 来执行您需要的加密操作(假设它们有些标准),而无需在 HSM 之外公开未加密的密钥。

于 2013-05-12T08:51:36.130 回答
1

如果您的平台支持 POSIX,您可能希望使用它mlock来防止您的数据被分页到交换区域。如果您正在为 Windows 编写代码,则可以VirtualLock改用。

请记住,没有绝对的方法可以保护敏感数据不被泄露,如果您要求数据在 RAM 中的任何时间点处于未加密形式(我们在这里谈论的是普通的 ol' RAM,没什么特别的像TrustZone)。您所能做的(和希望的)就是尽量减少数据保持未加密的时间,以便攻击者有更少的时间对其采取行动。

于 2013-05-11T18:56:54.357 回答
0

正如提到的其他答案,您可以实施软件解决方案,但如果您的程序在通用机器和操作系统上运行并且攻击者可以访问您的机器,它将无法保护您的敏感数据。如果您的数据非常敏感并且攻击者可以物理访问机器,那么一般的软件解决方案是不够的。

我曾经看到一些平台处理非常敏感的数据,这些平台有一些传感器来检测某人何时物理访问机器,并且在这种情况下会主动删除数据。

您已经提到过冷启动攻击,问题是 RAM 中的数据可以在通用 RAM 关闭几分钟后才能访问。

于 2013-05-11T19:13:38.927 回答
0

如果您的应用程序是用户模式应用程序,并且您尝试保护的内存来自其他用户模式进程,请尝试 CryptProtectMemory api(不适用于持久数据)。

于 2013-06-07T16:57:57.413 回答