如何安全地清除 std::string?
如何在 std::string
中存储敏感数据(例如:密码)?
我有一个应用程序,它提示用户输入密码并在连接设置期间将其传递到下游服务器。我想在建立连接后安全地清除密码值。
如果我将密码存储为 char *
数组,我可以使用 SecureZeroMemory 从进程内存中删除敏感数据。但是,我想在代码中避免使用 char 数组,并且正在寻找与 std::string
类似的内容?
How does one store sensitive data (ex: passwords) in std::string
?
I have an application which prompts the user for a password and passes it to a downstream server during connection setup. I want to securely clear the password value after the connection has been established.
If I store the password as a char *
array, I can use APIs like SecureZeroMemory to get rid of the sensitive data from the process memory. However, I want to avoid char arrays in my code and am looking for something similar for std::string
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
根据此处给出的答案,我编写了一个分配器来安全地归零记忆。
然而,事实证明,根据
std::string
的实现方式,分配器甚至可能不会为小值调用。例如,在我的代码中,甚至不会为字符串bar
调用deallocate
(在 Visual Studio 上)。那么答案是我们不能使用 std::string 来存储敏感数据。当然,我们可以选择编写一个处理用例的新类,但我对使用定义的 std::string 特别感兴趣。
感谢大家的帮助!
Based on the answer given here, I wrote an allocator to securely zero memory.
However, it turns out, depending on how
std::string
is implemented, it is possible that the allocator isn't even invoked for small values. In my code, for example, thedeallocate
doesn't even get called for the stringbar
(on Visual Studio).The answer, then, is that we cannot use std::string to store sensitive data. Of course, we have the option to write a new class that handles the use case, but I was specifically interested in using
std::string
as defined.Thanks everyone for your help!
openssl 经历了几次安全擦除字符串的迭代,直到决定采用这种方法:
openssl went through a couple of iterations of securely erasing a string until it settled on this approach:
这是一个复杂的话题,因为优化编译器会对你不利。像循环字符串和覆盖每个字符这样的直接方法并不可靠,因为编译器可能会优化它。与
memset
相同,但是,C11 添加了 memset_s,它应该是安全的,但可能不适用于所有平台。因此,我强烈建议使用可信的加密库来完成该任务,并让其作者负责可移植性。安全擦除是一项基本操作(获取 C 数组并安全地覆盖它),所有库都必须在某个时候实现它。请注意,
std::string
中的基础数据是连续的(如 C++11 标准所规定的 ,但实际上即使在 C++98/03 中你也可以假设它)。因此,您可以通过将std::string
作为数组来使用加密库的安全擦除功能。在 OpenSSL 中,安全擦除由 OPENSSL_cleanse 函数提供。 Crypto++ 有
memset_z
:顺便说一句,如果您从头开始设计 API,请考虑在存储机密时完全避免使用
std::string
。 std::string 的设计目标并不是防止泄露秘密(或在调整大小或复制过程中泄露秘密的一部分)。It is a complicated topic, as an optimizing compiler will work against you. Straightforward approaches like looping over the string and overwriting each character are not reliable, as the compiler might optimize it away. Same with
memset
, however, C11 added memset_s, which should be secure but might not be available on all platforms.For that reason, I would strongly recommend to use a trusted crypto library for that task and let their authors take care of portability. Secure wiping is a basic operation (taking a C-array and overwriting it securely), which all libraries will have to implement at some point. Note that the underlying data in a
std::string
is contiguous (as mandated by the C++11 standard, but in practice even in C++98/03 you could assume it). Therefore, you can use the secure wiping facilities of the crypto library by treading thestd::string
as an array.In OpenSSL, secure wiping is provided by the
OPENSSL_cleanse
function. Crypto++ hasmemset_z
:As a side-note, if you design the API from scratch, consider avoiding
std::string
altogether when it comes to storing secrets. It was not a design goal ofstd::string
to prevent leaking the secret (or parts of it during resizing or copying).为了后代,我曾经决定忽略这个建议并使用 std::string ,并使用 c_str() (并抛弃常量性)和易失性编写一个 Zero() 方法。如果我很小心,没有导致内容的重新分配/移动,并且我在需要清理的地方手动调用了zero(),那么一切似乎都正常运行。唉,我发现了另一个严重的缺陷:std::string也可以是引用计数的对象...在c_str()处爆破内存(或引用对象指向的内存)会在不知不觉中爆破另一个对象。
For posterity, I once decided to ignore this advice and use std::string anyway, and wrote a zero() method using c_str() (and casting away the constness) and volatile. If I was careful and didn't cause a reallocate/move of the contents, and I manually called zero() where I needed it clean, all seemed to function properly. Alas, I discovered another serious flaw the hard way: std::string can also be a referenced-counted object... blasting the memory at c_str() (or the memory the referenced object is pointing to) will unknowingly blast the other object.
对于 Windows:
这将根据 STL 内部结构安全地清除堆栈或堆中的数据。
适用于所有尺寸的琴弦,无论大小。
注意!
请勿使用
ptr
更改字符串的数据,这可能会导致长度增加或减少。For Windows:
This shall securely clear the data from the stack or the heap depending on the STL internals.
Works on all sizes of the string no matter small or large.
Caution !
DO NOT USE
ptr
for altering the data of the string which might result in increasing or decreasing the length.std::string 基于 char*。作为 char* 的所有动态魔法背后的某个地方。因此,当您说不想在代码中使用 char* 时,您仍然在使用 char*,它只是在后台,上面堆满了一大堆其他垃圾。
我对进程内存不太有经验,但您始终可以迭代每个字符(在加密并将密码存储在数据库中之后?),并将其设置为不同的值。
还有一个 std::basic_string,但我不确定这会对您有什么帮助。
std::string is based on a char*. Somewhere behind all the dynamic magic as a char*. So when you say you don't want to use char*'s on your code, you are still using a char*, it's just in the background with a whole bunch of other garbage piled on top of it.
I'm not too experienced with process memory, but you could always iterate through each character (after you've encrypted and stored the password in a DB?), and set it to a different value.
There's also a std::basic_string, but I'm not sure what help that would do for you.
或者更好地编写自己的函数:
or even better write your own function: