在发送到数据库之前将密码存储在字符串中

Storing a password in a string before sending to database

我正在努力学习一些处理密码的好习惯。我将提供我项目中的一些代码片段,并解释我担心和好奇的地方。

让我们从获取用户输入的代码开始,我的按钮事件代码:

string username = txtUser.Text;
string password = Hash.EncryptString(txtPass.Text);

我的想法是,以明文形式将密码存储在字符串中可能是一种不好的做法?我知道这可能不是解决方案(特别是因为我将它以明文形式发送到另一个方法,然后无论如何将它存储在一个字符串中),但在这里我调用了一个我创建的方法密码变成哈希。 "Hash" class:

中的EncryptString方法
   public static string EncryptString(string text) {
        var sha1 = System.Security.Cryptography.SHA1.Create();
        var inputBytes = Encoding.ASCII.GetBytes(text);
        text = ""; //clear string
        var hash = sha1.ComputeHash(inputBytes);

        var sb = new StringBuilder();
        for (var i = 0; i < hash.Length; i++)
            sb.Append(hash[i].ToString("X2"));

        return sb.ToString();
    }

这里就不多说了,我把密码用SHA1加密做了一个散列。我认为在使用后清除字符串会很聪明,这样密码就不再存储了?

稍后在我验证或添加用户的代码中,我获取或创建一个唯一的盐并将其与散列密码混合并再次使用 EncryptString 方法,然后再提交到数据库。

以隐私和安全的名义,这是好的做法吗?或者更确切地说,目前我的代码中存在哪些漏洞,我该如何修复它们?

您要防范什么情况?您在这里受到威胁的主要场景是包含字符串密码的内存转储,或其他内存分析调试工具。现在,可能是合法的威胁,也可能不是,这取决于 很多 更多的上下文。但是,如果 txtPass.Text 是一个客户端控件,那么坦率地说,当记忆工具开始发挥作用时,您将面临更大的风险,即键盘记录器会简单地为每个应用程序获取输入 输入的内容。

注意:

text = ""; //clear string

不会从非托管堆中删除字符串。它只是将名为 text 的变量中的 reference 更改为内部零长度字符串,又名 string.Emptyactual 字符串仍然存在于托管堆上,并且可能仍然存在于与输入机制相关的各种非托管位置 underneath txtPass.

这里有两个问题:

  1. 您所知道的 -- 堆中未受保护的内存,以及
  2. 你不知道的那个——你不应该散列密码 使用 SHA1。

让我们来解决这两个问题:

(1)很多安全人员会在youtube上推荐SecureString to protect your heap memory. However, it turns out that SecureString is not quite as good as advertised. If you want to understand why, you can watch this SecureString design review。它很长,但是很精彩,你真的只需要看 10 或 15 分钟就可以看出它的问题。

在 Web 应用程序的特定上下文中,您可以尝试各种特技来防止明文密码存储在内存中,但最终您会从 Request Object.您无法控制该请求对象的垃圾回收。拿到手后试图保护它就像在筛子上贴创可贴。

底线:别担心。您无法解决该框架固有的问题。

(2) 您关于密码存储的想法在 Top 10 Developer Crypto Mistakes 中排名第 4。

Troy Hunt 有一篇很棒的文章,展示了获得数据库访问权限的人如何破解密码,以及如何使用 bcrypt 或 pbkdf2(bcrypt 更好)来防止此类攻击。