这是在 Db 中加盐和存储密码的方法吗?

发布于 2024-10-26 17:07:25 字数 968 浏览 1 评论 0原文

有几种方法(即使在SO中),它们都提到将密码保留在数据库上的最佳方法是保存,不是密码,不是哈希密码,而是存储加盐密码的哈希值。

我的问题很简单,放一些代码就可以了,这是正确的方法吗?

string username = "myUsr";
string password = "myPwd";
DateTime createDate = DateTime.UtcNow;

// Salt it
string saltedPwd = String.Concat(password, createDate.Ticks.ToString());

// Hash it
HMACSHA1 hash = new HMACSHA1(Encoding.Unicode.GetBytes(Helper.EncryptKey));
string encodedPwd = Convert.ToBase64String(
                        hash.ComputeHash(Encoding.Unicode.GetBytes(saltedPwd)));

// Create User in the database
db.CreateUser(username, encodedPwd, createDate);

数据库用户表

user_id | username | password | create_date | last_access | active

和登录使用时再次执行该过程,并检查encodedPwd是否与提供的加盐、哈希密码相同。

我唯一关心的是,这是加盐密码的最佳方法吗?使用创建日期是否可以(因为它总是会改变,而且我读到最好始终使用不同的 <每次我们对密码进行编码时,code>salt...

或者salt应该是一个完全不同的变量吗?

There are seveal ways (even here in SO) and they all mention that the best way to keep password on database is to save, not the password, not the hased password, but to store the hash of a salted password.

My question is simple, putting some code on it, is this the correct way?

string username = "myUsr";
string password = "myPwd";
DateTime createDate = DateTime.UtcNow;

// Salt it
string saltedPwd = String.Concat(password, createDate.Ticks.ToString());

// Hash it
HMACSHA1 hash = new HMACSHA1(Encoding.Unicode.GetBytes(Helper.EncryptKey));
string encodedPwd = Convert.ToBase64String(
                        hash.ComputeHash(Encoding.Unicode.GetBytes(saltedPwd)));

// Create User in the database
db.CreateUser(username, encodedPwd, createDate);

Database User Table

user_id | username | password | create_date | last_access | active

and upon Login use do the process again and check if the encodedPwd is the same as the salted, hased password that was provided.

My only concern is, is this the best way to salt a password? Is it ok to use the Created Date (as that will always change, and I read that it is best to use always a different salt every time we encode a password...

Or should be the salt a completely different variable?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

别忘他 2024-11-02 17:07:25

您的实现可能足够好,但最好使用具有更多熵的盐:您当前使用的刻度值将始终在相对较小的范围内。

我建议使用类似 PBKDF2 的东西来为您完成工作,通过 Rfc2898DeriveBytes

string username = "myUsr";
string password = "myPwd";

using (var deriveBytes = new Rfc2898DeriveBytes(password, 20)) // 20-byte salt
{
    byte[] salt = deriveBytes.Salt;
    byte[] key = deriveBytes.GetBytes(20); // 20-byte key

    string encodedSalt = Convert.ToBase64String(salt);
    string encodedKey = Convert.ToBase64String(key);

    // store encodedSalt and encodedKey in database
    // you could optionally skip the encoding and store the byte arrays directly
    db.CreateUser(username, encodedSalt, encodedKey);
}

并进行身份验证...

string username = "myUsr";
string password = "myPwd";

string encodedSalt, encodedKey;
// load encodedSalt and encodedKey from database for the given username
byte[] salt = Convert.FromBase64String(encodedSalt);
byte[] key = Convert.FromBase64String(encodedKey);

using (var deriveBytes = new Rfc2898DeriveBytes(password, salt))
{
    byte[] testKey = deriveBytes.GetBytes(20); // 20-byte key

    if (!testKey.SequenceEqual(key))
        throw new InvalidOperationException("Password is invalid!");
}

Your implementation is probably good enough, but it would be better to use a salt with more entropy: the ticks value that you're currently using will always be in a relatively small range.

I would suggest using something like PBKDF2 to do the work for you, via Rfc2898DeriveBytes:

string username = "myUsr";
string password = "myPwd";

using (var deriveBytes = new Rfc2898DeriveBytes(password, 20)) // 20-byte salt
{
    byte[] salt = deriveBytes.Salt;
    byte[] key = deriveBytes.GetBytes(20); // 20-byte key

    string encodedSalt = Convert.ToBase64String(salt);
    string encodedKey = Convert.ToBase64String(key);

    // store encodedSalt and encodedKey in database
    // you could optionally skip the encoding and store the byte arrays directly
    db.CreateUser(username, encodedSalt, encodedKey);
}

And to authenticate...

string username = "myUsr";
string password = "myPwd";

string encodedSalt, encodedKey;
// load encodedSalt and encodedKey from database for the given username
byte[] salt = Convert.FromBase64String(encodedSalt);
byte[] key = Convert.FromBase64String(encodedKey);

using (var deriveBytes = new Rfc2898DeriveBytes(password, salt))
{
    byte[] testKey = deriveBytes.GetBytes(20); // 20-byte key

    if (!testKey.SequenceEqual(key))
        throw new InvalidOperationException("Password is invalid!");
}
沉默的熊 2024-11-02 17:07:25

我想知道为什么还没有人提到 BCrypt 。有一个现成的 C# 实现。请参阅http: //derekslager.com/blog/posts/2007/10/bcrypt-dotnet-strong-password-hashing-for-dotnet-and-mono.ashx

如果有适合您的经过验证的解决方案,请不要重新发明轮子问题。

I'm wondering why nobody has mentioned BCrypt yet. There is a ready to use implementation for C#. See http://derekslager.com/blog/posts/2007/10/bcrypt-dotnet-strong-password-hashing-for-dotnet-and-mono.ashx

Don't reinvent the wheel if there are proofed solutions for your issue.

当爱已成负担 2024-11-02 17:07:25

您的方法完全没问题,但是假设有人获得了您的数据库,但没有获得您的代码库。他们基本上可以发现您只是将密码和创建日期连接起来,然后他们就可以对所有密码进行逆向工程。

您可能希望进一步注入一个仅存在于您的代码库中的唯一字符串,以获得一点额外的保护。

string username = "myUsr";
string password = "myPwd";
DateTime createDate = DateTime.UtcNow;

// Salt it
string saltedPwd = String.Concat(password, SomeOtherClass.StaticKey, createDate.Ticks.ToString());

public class SomeOtherClass
{
    public static string StaticKey = "#$%#$%superuniqueblahal$#%@#$43580"; // should probably be const/readonly, but whatever
}

Your method is totally fine, but let's say someone got your database, but not your code base. They could essentially figure out that you simply concatenated the password and create date, and they could reverse engineer all passwords.

You may want to further inject a unique string that only exists in your code base for a little extra protection.

string username = "myUsr";
string password = "myPwd";
DateTime createDate = DateTime.UtcNow;

// Salt it
string saltedPwd = String.Concat(password, SomeOtherClass.StaticKey, createDate.Ticks.ToString());

public class SomeOtherClass
{
    public static string StaticKey = "#$%#$%superuniqueblahal$#%@#$43580"; // should probably be const/readonly, but whatever
}
临走之时 2024-11-02 17:07:25

您尝试一下: ProtectedData.Protect方法?

此方法可用于加密数据,例如密码、密钥或连接字符串。 可选Entropy参数使您能够添加数据以增加加密的复杂性;指定 null 不会增加复杂性。如果提供,则在使用取消保护方法解密数据时也必须使用此信息。

How about you try: ProtectedData.Protect method?

This method can be used to encrypt data such as passwords, keys, or connection strings. The optionalEntropy parameter enables you to add data to increase the complexity of the encryption; specify null for no additional complexity. If provided, this information must also be used when decrypting the data using the Unprotect method.

风月客 2024-11-02 17:07:25

我认为 CreateDate 的想法足够强大,但是当有人窃取您的数据库和代码时,您的盐就会暴露。基于“没有人可以篡改我的代码”的安全性是糟糕的安全性。

您可以简单地对密码进行双重散列...并使用第一次散列中的盐。

string Flavor(string passwd)
{
   string fhash = Str2SHA1(passwd);
   string salt = fhash[2] + fhash [10] + fhash[1]; // or whatever...
   string realhash = Str2SHA1(hash + salt);
}
string Str2Sha1(string str){ ... }

I think the idea with CreateDate is adequately powerful, but when someone steal your DB and Code, your salt is revealed. Security based on "nobody can snuff my code" is bad security.

You can simply double hash the password... And use salt from first hashing.

string Flavor(string passwd)
{
   string fhash = Str2SHA1(passwd);
   string salt = fhash[2] + fhash [10] + fhash[1]; // or whatever...
   string realhash = Str2SHA1(hash + salt);
}
string Str2Sha1(string str){ ... }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文