如何在RAM中安全地保存c++变量

how to keep c++ variables in RAM securely?

本文关键字:保存 c++ 变量 安全 RAM      更新时间:2023-10-16

我正在研究一个c++应用程序,它在RAM中保留了一些用户秘密密钥。这些密钥是高度敏感的。我必须把他们受到任何攻击的风险降到最低。
我使用字符数组来存储这些键,我读过一些关于在CPU寄存器甚至CPU缓存中存储变量的内容(即使用c++register关键字),但似乎没有保证的方法来强制应用程序在RAM之外存储一些变量(我的意思是在CPU寄存器或缓存中)。
有没有人可以建议一个好的方法来做到这一点,或者建议任何其他解决方案来保持这些密钥安全地在RAM中(我正在寻找一个独立于操作系统的解决方案)?

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

更一般地说,对于保护内存的问题,有一些特定于O/S的解决方案来指示块内存不应该被写入页面文件,例如Windows上的VirtualLock函数。如果你正在做加密并在内存中保存敏感数据,这些值得使用。

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

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

你不能消除风险,但你可以减轻它。

创建一个静态内存区域,这将是唯一存储明文键的地方。并创建一个随机数据缓冲区,您将使用它来取未存储在此静态缓冲区中的任何键。

当你从一个密钥文件或其他东西中读取一个键到内存中时,你只需要将它直接读取到这个静态缓冲区中,或者与你的随机数据一起复制到任何你需要它的地方,并立即用零清除缓冲区。

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

如果你需要对cleartext键进行操作,例如生成一个散列或验证他们的键,以某种方式将被屏蔽的xor键加载到这个静态缓冲区中,然后将其返回到cleartext并使用它。然后将0写回该缓冲区。

揭膜、操作、复刻的操作要快。不要让缓冲区长时间不带掩码。

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

在操作密钥时,甚至可以在需要验证密钥之前一次只揭开密钥的一个单词的掩码,这样就不会将完整的密钥存储在该缓冲区中。

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

短语"通过模糊实现安全"通常被误解。在安全算法的形式化分析中,"模糊"或隐藏非加密安全数据的方法不会增加加密算法的形式化安全性。在这种情况下是正确的。假设密钥存储在用户的机器上,并且必须由该机器上的程序使用,那么无法做任何事情来使该机器上的密钥具有加密安全性。无论您使用什么进程来隐藏或锁定数据,在某些时候程序必须使用它,并且一个有决心的黑客可以在代码中设置断点并监视程序何时使用数据。但这篇文章中没有任何建议可以消除这种风险。

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

但这并不意味着你不能做任何事情来降低风险。以最简单的访问密钥——密码为例。如果你对一台机器有物理访问权限,你可以安装一个键盘记录器,或者获取运行程序的内存转储等。因此,从形式上讲,密码并不比将其以明文形式写在粘在键盘上的便利贴上更安全。然而,每个人都知道将密码保存在便利贴上是一个坏主意,而对于程序来说,将密码以明文形式回显给用户也是一个坏做法。因为实际上,这大大降低了攻击者的门槛。然而,形式上,带有密码的便利贴也同样安全。

我上面提出的建议具有真正的安全优势。除了安全密钥的"xor"屏蔽之外,其他细节都不重要。有一些方法可以让这个过程变得更好。锁定密钥将限制程序员必须考虑作为攻击向量的位置的数量。一旦密钥被保存下来,你就可以在你的程序中使用不同的密钥,你可以复制它们,把它们写入文件,通过网络发送等等。这些东西都不会危及您的程序,除非攻击者有xor缓冲区。所以只有一个缓冲区需要考虑。然后,您可以对系统中的每个其他缓冲区进行松弛。(你可以mlock或者VirtualLock那个缓冲区)

一旦您清除了xor缓冲区,您就永久安全地消除了攻击者从程序的内存转储中恢复任何密钥的任何可能性。这样就限制了密钥可以被找回的位置和时间。而且,您正在建立一个系统,使您可以轻松地使用密钥,而不必在每次对包含密钥的对象进行操作时担心密钥可能容易被恢复。

所以你可以想象一个系统,其中的键重新计数xor缓冲区,当所有的键都不再需要时,你零并删除xor缓冲区,所有的键都变得无效和不可访问,而不必跟踪它们,并担心内存页是否被交换了,仍然保存明文键。

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

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

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

您可以创建一些函数,如"分配安全内存"(默认调用malloc)和"释放安全内存"(默认调用memsetfree),然后使用它们。在需要做其他事情的平台上,您可能需要做其他事情(例如锁定内存以防止密钥出现在swap中)。

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

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

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

这些提供:

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

您可以使用HSM的API来执行您需要的加密操作(假设它们在某种程度上是标准的),而不必在HSM之外暴露未加密的密钥。

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

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

如果你的是一个用户模式应用程序和内存,你试图保护是从其他用户模式进程尝试CryptProtectMemory api(不是持久数据)。

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

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

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