在 C# 窗体中将密码存储更改为哈希存储

Changing Storage of Password to Storing of Hashes in C# Forms

我当前的项目正在使用 Microsoft 的 Membership 来管理用户的凭据。

目前,它以纯文本形式将密码存储到 SQL 数据库中。

我想改为存储哈希值,或者更改为 encrypt/hide 密码列。


当前代码:

protected void BtnSubmit_Click(object sender, EventArgs e)
    {
        Page.Validate();

        if (Page.IsValid)
        {
            NAME_OF_TABLE_Provider provider = new NAME_OF_TABLE_Provider();

            string userName = SPContext.Current.Web.CurrentUser.Name;
            MembershipUserCollection userMembership = Membership.GetAllUsers();

            MembershipUser membershipUser = userMembership[userName];

            if (membershipUser != null)
            {
                try
                {
                    membershipUser.ChangePassword(OldPassword.Text, NewPassword.Text);
                    ConfirmPanel.Visible = true;
                    InvalidPanel.Visible = false;
                    Message.InnerText = "The password is changed successfully";
                }
                catch (Exception ex)
                {
                    ConfirmPanel.Visible = false;
                    InvalidPanel.Visible = true;
                    InvalidPassword.InnerText = "The password is not strong. Please verify.";
                }
            }
        }
    }

#1 可能的(?)解决方案:HashBytes

INSERT INTO <tbl> (..., passwd) values (...., HashBytes('SHA1', @password))

SELECT HashBytes('SHA1', @password);

#2 可能(?)解决方案:C# 存储哈希

    static void Main(string[] args)
    {
        //Store a password hash:
        PasswordHash hash = new PasswordHash("password");
        byte[] hashBytes = hash.ToArray();
        //For testing purposes
        Console.WriteLine(Convert.ToBase64String(hashBytes));

        //Check password against a stored hash
        byte[] hashBytes2 = hashBytes;//read from store.
        PasswordHash hash2 = new PasswordHash(hashBytes2);

        if (!hash.Verify("password"))
        {
            throw new System.UnauthorizedAccessException();
        }
        else
        {
            Console.WriteLine("True");
        }
        Console.ReadLine();
    }

    }

    public sealed class PasswordHash
    {
        const int SaltSize = 16, HashSize = 20, HashIter = 10000;
        readonly byte[] _salt, _hash;
        public PasswordHash(string password)
        {
            new RNGCryptoServiceProvider().GetBytes(_salt = new byte[SaltSize]);
            _hash = new Rfc2898DeriveBytes(password, _salt, HashIter).GetBytes(HashSize);
        }
        public PasswordHash(byte[] hashBytes)
        {
            Array.Copy(hashBytes, 0, _salt = new byte[SaltSize], 0, SaltSize);
            Array.Copy(hashBytes, SaltSize, _hash = new byte[HashSize], 0, HashSize);
        }
        public PasswordHash(byte[] salt, byte[] hash)
        {
            Array.Copy(salt, 0, _salt = new byte[SaltSize], 0, SaltSize);
            Array.Copy(hash, 0, _hash = new byte[HashSize], 0, HashSize);
        }
        public byte[] ToArray()
        {
            byte[] hashBytes = new byte[SaltSize + HashSize];
            Array.Copy(_salt, 0, hashBytes, 0, SaltSize);
            Array.Copy(_hash, 0, hashBytes, SaltSize, HashSize);
            return hashBytes;
        }
        public byte[] Salt { get { return (byte[])_salt.Clone(); } }
        public byte[] Hash { get { return (byte[])_hash.Clone(); } }
        public bool Verify(string password)
        {
            byte[] test = new Rfc2898DeriveBytes(password, _salt, HashIter).GetBytes(HashSize);
            for (int i = 0; i < HashSize; i++)
                if (test[i] != _hash[i])
                    return false;
            return true;
        }

#3 可能(?)解决方案:Transact-SQL 加密单列

https://docs.microsoft.com/en-us/sql/relational-databases/security/encryption/encrypt-a-column-of-data?view=sql-server-2017#TsqlProcedure


请问正确的做法是什么?谢谢。

不言而喻,任何解决方案都比当前解决方案更好。

  • 第一个解决方案看起来很可靠,但我无法确定是否使用了盐。如果没有,那就是劣势。您现在也依赖于此数据库供应商,无法切换。

  • 第二个解决方案看起来不错,但我没有仔细看,您可能应该前往 CodeReview,他们会审查工作代码以寻求改进。

  • 最后的解决方案不是真正的解决方案。密码 hashed 而不是 exncrypted 是有原因的。如果 you 可以解密它们,那么访问系统并窃取它们的攻击者将与您一样获得解密手段。所以这是一层不便,而不是安全。

所以选择第二个,让别人检查它是否存在弱点或错误。