繁体   English   中英

读取密码后覆盖 std::cin 读取缓冲区

[英]Overwrite std::cin read-buffer after reading password

从 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 打开/关闭控制台回显以避免在屏幕上看到纯文本密码。

我意识到这个问题是非常相似的这一个,但它已经超过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..

请注意,这不是完整的代码,它需要链接 replxxCrypto++

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM