有几种方式(即使在这里也是如此)并且他们都提到在数据库上保留密码的最佳方法是保存,而不是密码,而不是密码,但要存储盐渍的哈希值密码
我的问题很简单,在其上加上一些代码,这是正确的方法吗?
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
是否与提供的盐渍密码相同。
我唯一担心的是,这是加密密码的最佳方式吗?是否可以使用创建日期(因为这将永远改变,我读到最好始终使用每次我们编码密码时都会有salt
个不同的内容......
或者salt
应该是一个完全不同的变量吗?
答案 0 :(得分:18)
您的实施可能已经足够好了,但最好使用具有更多熵的盐:您当前使用的滴答值始终处于相对较小的范围内。
我建议使用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!");
}
答案 1 :(得分:4)
我想知道为什么没人提到BCrypt。 C#有一个随时可用的实现。见http://derekslager.com/blog/posts/2007/10/bcrypt-dotnet-strong-password-hashing-for-dotnet-and-mono.ashx
如果有针对您的问题的校对解决方案,请不要重新发明轮子。
答案 2 :(得分:0)
你的方法完全没问题,但是假设某人有你的数据库,但不是你的代码库。他们基本上可以弄清楚你只是简单地连接密码和创建日期,他们可以对所有密码进行逆向工程。
您可能希望进一步注入一个仅存在于代码库中的唯一字符串,以获得额外的保护。
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
}
答案 3 :(得分:0)
您如何尝试:ProtectedData.Protect
方法?
此方法可用于加密密码,密钥或连接字符串等数据。 optionalEntropy参数使您可以添加数据以增加加密的复杂性 ;指定null,没有额外的复杂性。如果提供,则在使用Unprotect方法解密数据时也必须使用此信息。
答案 4 :(得分:0)
我认为使用CreateDate的想法非常强大,但是当有人窃取你的数据库和代码时,你的盐被揭露了。基于“没有人可以扼杀我的代码”的安全性是不好的安全性。
您可以简单地对密码进行双重哈希...并在第一次哈希中使用salt。
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){ ... }