问题描述
从std :: cin读取密码后
std::cout << "Password: ";
securestring pw{}; // secure string,wipes automatically on destruction
SetConsoleEcho(false); // hide input in terminal
std::getline(std::cin,pw); // input the password
SetConsoleEcho(true); // switch echo back on
密码将一直存储在cin(堆内存)的读取缓冲区中,直到被新的输入覆盖为止,并且可以很容易地被Process Hacker之类的工具嗅探,或者在发生内存转储时将其写入磁盘。
有人知道如何以理想的跨平台方式清除/覆盖std :: cin.rdbuf()吗?还是有一种方法可以避免首先使用缓冲区?
上面的代码使用securestring,它是通过Crypto ++的AllocatorWithCleanup实现的,后者在破坏内存时会擦除内存。 SetConsoleEcho可以打开/关闭控制台回显,以避免在屏幕上看到纯文本密码。
我意识到这个问题与this one非常相似,但是已经超过10年了,被接受的答案实际上并不能回答问题。
解决方法
链接的帖子提供了您需要的所有答案,但是不幸的是,没有一种方法可以安全地读取密码,然后清除密码,密码被暴露的风险为0。见
即使您立即在缓冲区上涂写,仍然有可能将密码写入磁盘。系统I / O缓冲区以及std :: cin所在的工作内存也可能会分页到磁盘。我以前开发的侦查软件可以准确地发现这些情况。
此外,如果恶意行为者可以访问PC,则可以使用键盘记录程序提取密码。
,还是有一种方法可以避免首先使用缓冲区?
我发现了一种跨平台的方式(Linux,Mac,Windows)如何使用readline替换库来避免cin的读取缓冲区。我使用了replxx,但其他readline替换库也可能会这样做。
我围绕replxx创建了一个简单的包装类PasswordInput
,该包装类在每次击键时都会清除其输入缓冲区,并使用Crypto ++的AllocatorWithCleanup将输入的密码存储在安全的字符串中。创建密码摘要后,安全字符串将自动擦除,因此不会在过程内存中保留普通密码的副本。
这一切都是为了避免在过程内存中使用普通密码的时间超过所需时间。它不能防止击键记录程序,也不能防止在计算摘要时进程崩溃时泄漏密码的可能性,也不能防止定时嗅探攻击或任何其他可能的攻击媒介。
到目前为止,我仅在Windows上进行过测试,并且仅使用Process Hacker进行了基本的内存嗅探测试。这些没有显示出普通密码的痕迹(与使用std :: cin相反)
头文件:
using SecureString =
std::basic_string<char,std::char_traits<char>,CryptoPP::AllocatorWithCleanup<char>>;
class PasswordInput {
public:
PasswordInput();
void Read(const std::string& prompt);
inline const SecureString& Secret() const { return secret_; }
private:
void RxPwModifyCb(std::string& line,int& pos);
Replxx rx_;
SecureString secret_;
};
实施:
namespace ph = std::placeholders;
PasswordInput::PasswordInput() :
rx_{}
{
rx_.set_modify_callback(std::bind(&PasswordInput::RxPwModifyCb,this,ph::_1,ph::_2));
}
void PasswordInput::Read(const std::string& prompt)
{
char const* cinput{nullptr};
do
{
cinput = rx_.input(prompt);
} while ((cinput == nullptr) && (errno == EAGAIN));
}
void PasswordInput::RxPwModifyCb(std::string& line,int& pos)
{
if (!line.empty())
{
secret_.append(1,line[0]);
line.clear();
}
}
用法:
PasswordInput pwi{}; // create password input handler
pwi.Read("Password: "); // read the password
std::cout << "\x1b[A" << "\33[2K\r"; // go one line up,delete line and go to beginning (does nothing in terms of security but looks cool)
CalculatePassphrase(pwi.Secret().c_str(),pwi.Secret().size()); // Scrypt,pbkdf2,etc..