Is this the way to salt and store a Password in Db

2019-01-16 21:00发布

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?

5条回答
地球回转人心会变
2楼-- · 2019-01-16 21:23

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.

查看更多
▲ chillily
3楼-- · 2019-01-16 21:25

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.

查看更多
Anthone
4楼-- · 2019-01-16 21:31

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){ ... }
查看更多
聊天终结者
5楼-- · 2019-01-16 21:36

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!");
}
查看更多
ら.Afraid
6楼-- · 2019-01-16 21:38

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
}
查看更多
登录 后发表回答