如何将 c++ 变量安全地保存在 RAM 中?
Posted
技术标签:
【中文标题】如何将 c++ 变量安全地保存在 RAM 中?【英文标题】:how to keep c++ variables in RAM securely? 【发布时间】:2013-05-11 18:44:28 【问题描述】:我正在开发一个 C++ 应用程序,该应用程序将一些用户密钥保存在 RAM 中。这个密钥非常敏感,我必须尽量减少对它们进行任何类型攻击的风险。
我正在使用字符数组来存储这些键,我已经阅读了一些关于在 CPU 寄存器甚至 CPU 缓存中存储变量的内容(即使用 C++ register
关键字),但似乎没有保证强制应用程序的方法将其中一些变量存储在 RAM 之外(我的意思是在 CPU 寄存器或缓存中)。
任何人都可以提出一个好的方法来做到这一点或提出任何其他解决方案来将这些密钥安全地保存在 RAM 中(我正在寻找一个独立于操作系统的解决方案)?
【问题讨论】:
您问的问题对吗?在我看来,如果您担心密钥从易失性 RAM 中被盗,那么您最好花费精力来保护硬件的物理位置。 如果我担心物理位置,那是因为我知道从 RAM 中窃取敏感数据要容易得多(即使用冷启动攻击),但实际上存储位置并不是主要的问题如果我能保证RAM中数据的安全,那我就满足了! @EhsanKhodarahmi 你被误导了——你绝对不能保证任何未加密信息的安全,无论其位置如何(即RAM、缓存、注册,无论如何)在运行通用操作系统的通用机器上。 只要攻击者能够访问使用密钥的程序运行所在的机器,他就可以获得密钥。 如果您担心某些攻击者会读取内存,则根本不应该将密钥存储在内存中,而应专注于使用不需要它的方法。例如,您可以使用单向哈希来确保密码不以明文形式存储,但您仍然可以查看用户是否输入了正确的密码。 【参考方案1】:你的意图可能是崇高的,但它们也被误导了。简短的回答是,在 通用 系统(即商品处理器/主板和通用 O/S)上确实没有办法做你想做的事。即使您可以以某种方式强制将内容仅存储在 CPU 上,它仍然无济于事。这只是一个小麻烦。
对于保护内存的更一般的问题,有特定于操作系统的解决方案表明不应将块内存写入页面文件,例如 Windows 上的 VirtualLock
函数。如果您正在执行加密并在该内存中保存敏感数据,那么这些是值得使用的。
最后一件事:我要指出让我担心的是,您对 register
关键字及其安全含义存在根本性的误解;请记住,这是一个提示,它不会 - 实际上,它不能 - 强制将任何内容实际存储在寄存器或其他任何地方。
现在,这本身并不是什么大问题,但在这里却是一个问题,因为它表明您并没有真正掌握安全工程或风险分析,如果您是设计或实施真实世界的加密解决方案。坦率地说,您的帖子表明(至少对我而言)您还没有准备好构建或实施这样的系统。
【讨论】:
我会说,如果他们在注册表中,它会使攻击系统更容易。如果您知道数据在哪里,那就成功了一半。 RAM 与 CPU 相比的唯一优势是有很多需要搜索的空间(但同样,如果你知道它在哪里,那么提取它就不再困难了(使用正确的工具)。 @LokiAstari 我完全同意 - 正如我所说,OP 被误导了。 OP 不寻求安全保证,只寻求最小化。你太苛刻了,尤其是说他没有资格。我们都在这里学习。 @Freedom_Ben 他担心的风险是不可能使用软件或语言关键字进行管理。指出这一点并不苛刻,或者告诉他他提出了错误的问题并在不成立的假设上运作。【参考方案2】:风险无法消除,但可以减轻。
创建一个单独的静态内存区域,该区域将是您存储明文密钥的唯一位置。并创建一个随机数据缓冲区,您将使用它来异或任何未存储在这个静态缓冲区中的键。
每当您将密钥从密钥文件或其他内容读入内存时,您只需将其直接读入这个静态缓冲区,与随机数据进行异或并将其复制到您需要的任何地方,然后立即用零清除缓冲区。
您可以通过比较它们的掩码版本来比较任意两个键。您甚至可以比较屏蔽键的哈希值。
如果您需要对明文密钥进行操作 - 例如生成哈希或验证他们的密钥以某种方式将屏蔽的异或密钥加载到这个静态缓冲区中,将其异或返回到明文并使用它。然后将零写回该缓冲区。
unmasking、operating 和 remasking 的操作应该很快。不要让缓冲区长时间处于未屏蔽状态。
如果有人尝试进行冷启动攻击,拔掉硬件插头并检查内存芯片,那么只有一个缓冲区可以保存明文密钥,而且在冷启动的特定时刻很有可能攻击缓冲区将是空的。
在对密钥进行操作时,您甚至可以在需要验证密钥之前一次只取消屏蔽密钥的一个字,这样完整的密钥就不会存储在该缓冲区中。
@update:我只是想解决以下 cmets 中的一些批评:
“通过默默无闻的安全”这一短语通常被误解。在安全算法的形式分析中,“模糊性”或隐藏非密码安全数据的方法不会增加密码算法的形式安全性。在这种情况下确实如此。鉴于密钥存储在用户机器上,并且必须由该机器上的该程序使用,因此无法使该机器上的密钥加密安全。无论您使用什么过程来隐藏或锁定数据,程序都必须在某些时候使用它,而坚定的黑客可以在代码中设置断点并观察程序何时使用数据。但是这个帖子中没有任何建议可以消除这种风险。
有人建议 OP 找到一种方法来使用带有锁定内存芯片的特殊硬件或锁定芯片的某种操作系统方法。这在密码学上不再安全。最终,如果您对机器有物理访问权,那么足够坚定的黑客可以使用内存总线上的逻辑分析仪并恢复任何数据。此外,OP 已经声明目标系统没有这样的专用硬件。
但这并不意味着您无法采取任何措施来降低风险。使用最简单的访问密钥——密码。如果您对机器有物理访问权限,则可以放入键盘记录器,或获取正在运行的程序的内存转储等。因此,从形式上讲,密码并不比以明文形式写在粘在键盘上的便签上更安全。然而,每个人都知道在便签上保留密码是一个坏主意,而且对于程序以明文形式向用户回显密码是不好的做法。当然,实际上,这大大降低了攻击者的门槛。然而,正式的带有密码的便签也同样安全。
我上面提出的建议具有真正的安全优势。除了安全密钥的“异或”屏蔽之外,任何细节都不重要。并且有一些方法可以使这个过程变得更好一些。异或密钥将限制程序员必须考虑作为攻击向量的位置数量。一旦密钥被异或,您可以在整个程序中拥有不同的密钥,您可以复制它们,将它们写入文件,通过网络发送它们等。除非攻击者拥有异或缓冲区,否则这些事情都不会危及您的程序。所以有一个你必须担心的单一缓冲区。然后,您可以放松系统中的所有其他缓冲区。 (并且您可以 mlock 或 VirtualLock 那个缓冲区)
一旦您清除了异或缓冲区,您就可以永久且安全地消除攻击者从您程序的内存转储中恢复任何密钥的可能性。您正在限制您的曝光,无论是地点数量还是可以恢复密钥的时间。而且您正在建立一个系统,该系统允许您轻松使用密钥,而无需在对包含密钥的对象进行每次操作时担心恢复密钥的可能简单方法。
因此,您可以想象一个系统,其中键引用异或缓冲区,当不再需要所有键时,您将异或缓冲区归零并删除,所有键都变得无效和不可访问,而无需跟踪它们并担心关于内存页面是否被换出并仍然保存明文密钥。
您也不必真正保留随机数据的缓冲区。例如,您可以使用加密安全的随机数生成器,并根据需要使用单个随机种子来生成异或缓冲区。攻击者恢复密钥的唯一方法是访问单个生成器种子。
您还可以根据需要在堆栈上分配明文缓冲区,并在完成后将其清零,这样堆栈极不可能留在芯片缓存中。如果完整的密钥从未被解码,而是根据需要一次解码一个单词,即使访问堆栈缓冲区也不会泄露密钥。
【讨论】:
优秀的答案。这是迄今为止最好的一个。 +1,因为我只能投票一次。 这绝对没有任何意义。由于多种原因,XOR 方案是愚蠢的。一是攻击者所要做的就是找到存储异或主数据的位置,进行异或运算,仅此而已。另一个是它完全容易受到已知明文等各种攻击。它可能不会造成任何伤害,前提是没有人依赖它并且也做了其他人建议的各种理智的事情——但如果有人期望它提供某种程度的安全性并依赖它,就会发生非常糟糕的事情。 这里发布的一些批评似乎是“没有很好的解决方案,所以你甚至不应该尝试”。大概你也不会锁上你家的前门,因为打破窗户并以这种方式进入是微不足道的...... @david:你说“有更好的解决方案”——但是你建议使用“mlock”。但这提供了零额外的加密安全性。在这种情况下,他担心冷启动攻击——但如果你可以启动系统——你可以启动任何你想要的东西——所以你甚至不需要 root/admin 访问来扫描内存。所有的密钥都将以明文形式存在于内存中。你也错了这容易受到已知的明文攻击 - 掩码位可以随心所欲地改变。 @RafaelBaptista:我不会在 SO cmets 中与您讨论这个问题。这是一个真正的坏主意,你让自己看起来很愚蠢,为它辩护。它容易受到已知明文攻击,因为如果您知道密钥的明文,您可以将明文密钥与 XORed 密钥进行异或并恢复掩码。如果这是个好主意,真正的程序会以这种方式做事,而它们不会。【参考方案3】:没有独立于平台的解决方案。您要解决的所有威胁都是特定于平台的,因此解决方案也是如此。没有法律要求每个 CPU 都有寄存器。没有法律要求 CPU 具有缓存。其他程序访问您程序的 RAM 的能力,实际上是其他程序的存在,都是平台细节。
您可以创建一些函数,例如“分配安全内存”(默认调用malloc
)和“释放安全内存”(默认调用memset
然后free
)然后使用它们。您可能需要在需要其他操作的平台上执行其他操作(例如锁定内存以防止您的密钥在交换中结束)。
【讨论】:
&如果我限制在 windows 操作系统和 intel x86 架构,你有什么建议? 附加的东西是特定于每个平台的。 Windows 有VirtualLock
,一些UNIX 有mlock
。【参考方案4】:
除了上面非常好的 cmets,您必须考虑即使 IF 您成功地将密钥存储在寄存器中,当中断时,寄存器内容很可能会存储在内存中进来,和/或当另一个任务开始在机器上运行时。当然,可以物理访问机器的人可以运行调试器并检查寄存器。如果密钥足够重要以至于有人会在这样的设备上花费数千美元,那么调试器可能是“电路仿真器”——这意味着目标系统上根本没有软件。
另一个问题当然是这有多重要。密钥来自哪里?有人输入吗?如果不是,并且存储在其他地方(在代码中,服务器上等),那么它们将在某个时候存储在内存中,即使您在实际使用密钥时成功地将它们排除在内存之外。如果有人正在输入它们,那么某人以某种方式或另一种方式迫使知道密钥的人透露密钥是否存在安全风险?
【讨论】:
【参考方案5】:正如其他人所说,在通用计算机上没有安全的方法可以做到这一点。另一种方法是使用Hardware Security Module (HSM)。
这些提供:
对密钥的物理保护比普通 PC/服务器更好(防止直接访问 RAM); 更大的逻辑保护,因为它们不是通用的 - 机器上没有运行其他软件,因此没有其他进程/用户可以访问 RAM。您可以使用 HSM 的 API 来执行您需要的加密操作(假设它们有些标准),而无需将未加密的密钥暴露在 HSM 之外。
【讨论】:
我曾想过在我的答案中包含一个指向 IBM 加密处理器的链接,但我写的时候有点着急。感谢发帖。【参考方案6】:如果您的平台支持 POSIX,您可能希望使用mlock
来防止您的数据被分页到交换区。如果您正在为 Windows 编写代码,则可以改用 VirtualLock
。
请记住,如果您要求数据在 RAM 中的任何时间点处于未加密形式(我们在这里谈论的是普通的 ol' RAM , 没有什么像 TrustZone) 这样的花哨的东西。您所能做的(和希望的)就是尽量减少数据未加密的时间,以便攻击者有更少的时间对其采取行动。
【讨论】:
【参考方案7】:如果您的应用程序是用户模式应用程序,并且您尝试保护的内存来自其他用户模式进程,请尝试 CryptProtectMemory api(不适用于持久数据)。
【讨论】:
【参考方案8】:正如提到的其他答案,您可以实施软件解决方案,但如果您的程序在通用机器和操作系统上运行并且攻击者可以访问您的机器,则它不会保护您的敏感数据。如果您的数据确实非常敏感,并且攻击者可以物理访问机器,那么一般的软件解决方案是不够的。
我曾经看到一些平台处理非常敏感的数据,这些平台有一些传感器来检测有人何时物理访问机器,并且在这种情况下会主动删除数据。
您已经提到过冷启动攻击,问题是 RAM 中的数据可以在一般 RAM 上关闭几分钟后才能访问。
【讨论】:
以上是关于如何将 c++ 变量安全地保存在 RAM 中?的主要内容,如果未能解决你的问题,请参考以下文章