如何安全地将C++变量保留在RAM中?

14

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


2
你是否在问正确的问题?在我看来,如果你担心密钥被从易失性RAM中窃取,那么你的努力最好花在保护硬件的物理位置上。 - Eric Andres
8
@EhsanKhodarahmi 你的想法有误 -- 无论数据存在哪里(例如RAM、cache、register等),在普通计算机上运行普通操作系统时,你绝对无法保证任何未加密信息的安全。 - Nik Bougalis
4
只要攻击者可以访问运行使用该密钥程序的计算机,他就能够获取该密钥。 - typ1232
2
如果您担心某个攻击者读取内存,就不应该在内存中存储密钥,而应专注于使用不需要其存在的方法。例如,您可以使用单向哈希来确保密码不以明文形式存储,但仍然可以查看用户是否输入了正确的密码。 - Mats Kindahl
各位,楼主并不是在寻求保证,只是想要最小化。请仔细阅读问题。 - Freedom_Ben
显示剩余2条评论
8个回答

17
你的意图可能是崇高的,但也是误导的。简短的回答是,在通用系统(即商品处理器/主板和通用操作系统)上没有真正实现你想要的功能的方法。即使你能够以某种方式强制将内容仅存储在CPU上,它也不会真正有所帮助,只会是一个小麻烦。
更一般地说,关于保护内存的问题,有特定于操作系统的解决方案,指示块内存不应写入页面文件,例如Windows上的VirtualLock函数。如果你正在进行加密并在该内存中保存敏感数据,则值得使用这些解决方案。
最后一件事:我要指出让我担心的是,你对register关键字及其安全性影响存在基本误解;请记住,它只是一个提示,不能确保任何东西实际上都被存储在寄存器或其他任何地方。
现在,这本身并不是什么大问题,但是如果你正在设计或实施真实世界的加密解决方案,这是一个大问题,因为它表明你并没有真正掌握安全工程或风险分析。坦白地说,你的帖子让我觉得(至少对我来说)你还没有准备好设计或实施这样的系统。

4
如果它们在注册表中,我会说这使得攻击系统变得更容易。如果您知道数据的位置,那就已经成功了一半。与CPU相比,RAM的唯一优势是有很多可以搜索的内容(但如果您知道它的位置,使用正确的工具提取它并不更困难)。 - Martin York
@LokiAstari 我完全同意 - 正如我所说,原帖是误导性的。 - Nik Bougalis
14
楼主不是在寻求安全保证,只是寻求最小化。你的批评过于严厉,特别是说他没有资格。我们都在这里学习。 - Freedom_Ben
4
他担心的风险是不可能通过软件或语言关键词来管理。指出这一点并告诉他他正在提出错误问题和基于不成立假设操作并不是过分苛刻的。 - Nik Bougalis

12

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

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

每当您将密钥读入内存中,从密钥文件或其他地方,您仅将其直接读入此静态缓冲区中,与随机数据进行异或操作,并将其复制到所需位置,然后立即用零清除缓冲区。

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

如果您需要操作明文密钥-例如生成哈希或验证密钥,请将掩码异或密钥加载到此静态缓冲区中,将其异或回明文,并使用它。然后向该缓冲区写入零。

解除掩码、操作和重新掩码的操作应该很快。不要让缓冲区长时间保持未掩码状态。

如果有人尝试进行冷启动攻击,拔掉硬件插头并检查内存芯片,可能只有一个缓冲区可能保存明文密钥,并且在该特定瞬间的冷启动攻击期间,该缓冲区很可能为空。

在操作密钥时,您甚至可以在需要验证密钥之前每次仅解掩码一个密钥词,以便完整密钥永远不会存储在该缓冲区中。

@更新:我想回应下面评论中的一些批评。

“安全通过深度隐藏”这一说法常被误解。在对安全算法进行正式分析时,“深度隐藏”或者采用非加密技术隐藏数据的方法并不能提高加密算法的正式安全性。这种情况是真实存在的。鉴于密钥存储在用户计算机上,必须由该程序在该计算机上使用,因此无论您采用何种过程来隐藏或锁定数据,最终程序都必须使用它,一个决心强烈的黑客可以在代码中设置断点并监视程序何时使用数据。但是该线程中的任何建议都不能消除这种风险。
有些人建议原帖作者找到一种使用带有锁定存储芯片的特殊硬件或某种操作系统方法来锁定芯片的方式。从加密角度来看,这并不更安全。如果您能够物理访问机器,足够坚定的黑客可以在内存总线上使用逻辑分析仪并恢复任何数据。此外,原帖作者已经声明目标系统没有这样的专用硬件。
但这并不意味着您不能采取措施来减轻风险。以最简单的访问密钥-密码为例。如果您可以物理访问计算机,则可以插入键盘记录器或获取运行程序的内存转储等。因此,从正式角度来看,密码不比将其写在粘贴在键盘上的纸条上更安全。然而,每个人都知道将密码放在便利贴上是一个坏主意,而且让程序以明文形式回显密码也是一种不良实践。因为从实际上讲,这显著降低了攻击者的门槛。然而,从正式上说,用纸条写明密码并不会使其不安全。我提出的建议具有真正的安全优势。除了对安全密钥进行“异或”掩码之外,其他所有细节都不重要。而且有办法使这个过程变得更好。对密钥进行异或运算将限制程序员必须考虑作为攻击向量的位置数量。一旦密钥被异或,您可以在程序中随处使用不同的密钥,可以复制它们,将它们写入文件,通过网络发送等等。除非攻击者拥有异或缓冲区,否则这些事情都不会危及您的程序。因此,您只需要担心一个缓冲区。然后您就可以放松对系统中的其他缓冲区的担忧(并且您可以mlock或VirtualLock该一个缓冲区)。
一旦您清除了该异或缓冲区,您将永久且安全地消除了攻击者可以从程序的内存转储中恢复任何密钥的可能性。在限制密钥可以被恢复的位置和时间方面降低了您的风险暴露。并且您正在实施一个系统,使您可以轻松地使用密钥,而无需在包含密钥的对象上的每个操作期间担心可能易受攻击的方式。
因此,您可以想象一个系统,密钥引用异或缓冲区,并且当所有密钥不再需要时,您将清零并删除异或缓冲区,并且所有密钥都变得无效和无法访问,而无需追踪它们并担心内存页面是否仍然保留明文密钥。
您也不必逐字保留一个随机数据缓冲区。例如,您可以使用加密安全的随机数生成器,并使用单个随机种子根据需要生成异或缓冲区。攻击者可以恢复密钥的唯一方法是访问单个生成器种子。
您还可以根据需要在堆栈上分配明文缓冲区,并在完成后将其清零,这样极少有可能堆栈离开芯片缓存。如果未解码完整密钥,而是根据需要一个字一个字地解码,则即使访问堆栈缓冲区也不会揭示密钥。

3
非常优秀的回答,这是目前为止最好的一个。因为我只能点赞一次,所以给你加上+1。 - Freedom_Ben
2
这根本没有任何意义。XOR方案之所以愚蠢,原因有很多。其中一个原因是攻击者只需找到存储XOR主数据的位置,进行XOR操作,就可以轻松破解。另一个原因是它完全容易受到各种攻击,如已知明文攻击。如果没有人依赖它并且按照其他人建议的合理方法做事,它可能不会造成任何伤害 - 但是如果有人期望它提供一定程度的安全性并依赖它,那么非常糟糕的事情将会发生。 - David Schwartz
3
这里发布的一些批评似乎是“没有很好的解决方案,所以你甚至不应该尝试”的观点。大概你也不会锁前门,因为用砸窗户的方式可以轻松进入…… - jcoder
4
你说“有更好的解决方案”,但是你建议使用“mlock”。但这并没有提供额外的加密安全性。在这种情况下,他担心的是冷启动攻击——但是如果可以启动系统,就可以启动任何想要的东西,因此即使不需要root/admin访问权限也可以扫描内存。所有的密钥都将明文地存储在内存中。你也错了,认为这容易受到已知明文攻击——掩码位可以随意更改。 - Rafael Baptista
2
@RafaelBaptista:我不会在SO评论中与您辩论这个问题。这是一个真正糟糕的想法,如果您为其辩护,会让自己看起来很愚蠢。它确实容易受到已知明文攻击,因为如果您知道密钥的明文,您可以将明文密钥与异或密钥进行异或运算并恢复掩码。如果这是一个好主意,真正的程序会以这种方式处理事情,但他们没有这样做。 - David Schwartz
显示剩余10条评论

5

没有平台无关的解决方案。你处理的所有威胁都是特定于平台的,因此解决方案也是如此。没有法律要求每个CPU都有寄存器。没有法律要求CPU具有高速缓存。另一个程序访问您程序的RAM的能力,事实上其他程序的存在,都是平台细节。

你可以创建一些函数,比如“分配安全内存”(默认情况下调用malloc)和“释放安全内存”(默认情况下调用memset然后free),然后使用这些函数。在需要其他操作的平台上,您可能需要执行其他操作(例如锁定内存以防止密钥进入交换空间)。


如果我限制在Windows操作系统和Intel x86架构下,你有什么建议? - Ehsan Khodarahmi
1
额外的东西对于每个平台都是特定的。Windows 有 VirtualLock,一些 UNIX 系统有 mlock - David Schwartz

2
除了上面非常好的评论之外,您必须考虑到即使您成功地使密钥存储在寄存器中,当中断发生和/或另一个任务在机器上运行时,该寄存器内容很可能会被存储在内存中。当然,拥有物理访问权限的人可以运行调试器并检查寄存器。如果该密钥足够重要,某人将花费几千美元购买这样的设备,则调试器可能是“电路仿真器”,这意味着目标系统上没有任何软件。
另一个问题当然是这有多重要。密钥从哪里来?有人在输入吗?如果不是,并且在其他地方存储(在代码中,在服务器上等),则它们最终会在某个时候存储在内存中,即使您成功地在实际使用密钥时将它们排除在内存之外。如果有人在输入它们,那么安全风险不就是某种方式强迫知道密钥的人透露密钥吗?

2
正如其他人所说,一般计算机上没有安全的方法来执行此操作。替代方案是使用硬件安全模块(HSM)。
它们提供以下功能:
- 比普通PC /服务器具有更高的密钥物理保护性(防止直接访问RAM); - 具有更高的逻辑保护性,因为它们不是通用型的 - 机器上没有运行其他软件,因此没有其他进程/用户可以访问RAM。
您可以使用 HSM 的 API 执行所需的加密操作(假设它们是标准的),而不会将未加密的密钥暴露在 HSM 外部。

我本来想在我的回答中包含IBM加密处理器的链接,但当时有点匆忙。感谢您的发布。 - Nik Bougalis

1
如果您的平台支持POSIX,您需要使用mlock来防止数据被分页到交换区。 如果您要为Windows编写代码,则可以使用VirtualLock
请记住,如果您需要在RAM中的任何时间点以其未加密形式保留数据(我们正在谈论普通的RAM,没有像TrustZone这样的高级功能),则没有绝对的方法可以保护敏感数据不被泄露。您能做的(并希望)是尽量减少数据保持未加密状态的时间,以便对手有更少的时间来采取行动。

0
正如其他答案所提到的,您可以实现软件解决方案,但如果您的程序在通用计算机和操作系统上运行,并且攻击者可以访问您的计算机,则无法保护您的敏感数据。如果您的数据确实非常敏感,并且攻击者可以物理访问计算机,则一般软件解决方案将不足以应对。
我曾经看到一些处理非常敏感数据的平台,它们有一些传感器来检测当有人物理访问计算机时,并在这种情况下主动删除数据。
您已经提到了冷启动攻击,问题在于RAM中的数据可以在通用RAM上关机后几分钟内被访问。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接