如何在std::string
?
我有一个应用程序,提示用户输入密码,并在连接设置期间将其传递给下游服务器。我想在建立连接后安全地清除密码值。
如果我将密码存储为char *
数组,我可以使用SecureZeroMemory之类的API来清除进程内存中的敏感数据。但是,我想在我的代码中避免使用char数组,并且正在为std::string
寻找类似的东西?
答案 0 :(得分:12)
根据here给出的答案,我写了一个分配器来安全地记忆零。
#include <string>
#include <windows.h>
namespace secure
{
template <class T> class allocator : public std::allocator<T>
{
public:
template<class U> struct rebind { typedef allocator<U> other; };
allocator() throw() {}
allocator(const allocator &) throw() {}
template <class U> allocator(const allocator<U>&) throw() {}
void deallocate(pointer p, size_type num)
{
SecureZeroMemory((void *)p, num);
std::allocator<T>::deallocate(p, num);
}
};
typedef std::basic_string<char, std::char_traits<char>, allocator<char> > string;
}
int main()
{
{
secure::string bar("bar");
secure::string longbar("baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar");
}
}
然而,事实证明,根据std::string
的实现方式,甚至可能不会为小值调用分配器。例如,在我的代码中,deallocate
甚至没有调用字符串bar
(在Visual Studio上)。
答案是,我们不能使用std :: string来存储敏感数据。当然,我们可以选择编写一个处理用例的新类,但我特别感兴趣的是使用std::string
定义。
感谢大家的帮助!
答案 1 :(得分:2)
作为一个optimizing compiler will work against you,这是一个复杂的话题。简单的方法(例如遍历字符串和覆盖每个字符)并不可靠,因为编译器可能会对其进行优化。与memset
相同,但是C11添加了memset_s,它应该是安全的,但可能并非在所有平台上都可用。
由于这个原因,我强烈建议使用受信任的加密库来执行该任务,并让其作者注意可移植性。安全擦除是一项基本操作(获取C数组并对其进行安全覆盖),所有库都必须在某个时候实现。请注意,std::string
中的基础数据是连续的(与mandated by the C++11 standard一样,但是实际上即使在C ++ 98/03中,您也可以假设它是连续的)。因此,您可以通过将std::string
作为数组来使用加密库的安全擦除功能。
在OpenSSL中,OPENSSL_cleanse
函数提供了安全擦除。加密++具有memset_z
:
std::string secret;
// ...
// OpenSSL (#include <openssl/crypto.h> and link -lcrypto)
OPENSSL_cleanse(&secret[0], secret_str.size());
// Crypto++ (#include <crypto++/misc.h> and link -lcrypto++)
CryptoPP::memset_z(&secret[0], 0, secret.size());
作为补充,如果您从头开始设计API,则在存储秘密时,请考虑完全避免使用std::string
。防止泄漏机密(或在调整大小或复制过程中泄漏其一部分)不是std::string
的设计目标。
答案 2 :(得分:1)
std :: string基于char *。作为char *的所有动态魔法背后的某个地方。因此,当你说你不想在你的代码中使用char *时,你仍然使用char *,它只是在后台,其上堆满了一大堆其他垃圾。
我对进程内存不是很熟悉,但是你总是可以迭代每个字符(在你加密并将密码存储在数据库中之后?),并将其设置为不同的值。
还有一个std :: basic_string,但我不确定会对你有什么帮助。
答案 3 :(得分:1)
对于后代,我曾经决定忽略这个建议并且无论如何都使用std :: string,并使用c_str()(并抛弃const)和volatile编写了一个zero()方法。如果我小心并且没有导致重新分配/移动内容,并且我手动调用零()我需要它干净,所有似乎都正常运行。唉,我发现了另一个严重的缺陷:std :: string也可以是一个引用计数的对象...在c_str()(或引用的对象所指向的内存)中爆破内存会在不知不觉中爆炸另一个对象
答案 4 :(得分:0)
std::string mystring;
...
std::fill(mystring.begin(), mystring.end(), 0);
甚至更好地编写自己的函数:
void clear(std::string &v)
{
std::fill(v.begin(), v.end(), 0);
}
答案 5 :(得分:0)
openssl进行了几次安全擦除字符串迭代,直到采用此方法为止:
#include <string.h>
#include <string>
// Pointer to memset is volatile so that compiler must de-reference
// the pointer and can't assume that it points to any function in
// particular (such as memset, which it then might further "optimize")
typedef void* (*memset_t)(void*, int, size_t);
static volatile memset_t memset_func = memset;
void cleanse(void* ptr, size_t len) {
memset_func(ptr, 0, len);
}
int main() {
std::string secret_str = "secret";
secret_str.resize(secret_str.capacity(), 0);
cleanse(&secret_str[0], secret_str.size());
secret_str.clear();
return 0;
}