我的MVC3密码处理是否足够

我已经阅读了很多有关如何存储和比较输入的密码的文章,其中有些是旧的,有些是新的,但是都不同.因此,我选择了我认为是最好的选择,并实施了以下方法.

请注意,我创建了自己的身份验证提供程序,并且没有使用或继承标准MS MembershipProvider(太to肿).这是针对不需要“超级安全”的轻量级站点的,但是我想遵循最佳实践.我只想验证一下,我没有做任何明显的错误或打开安全漏洞.另外,我查看了每个用户的密码设置,但对于我的需求而言,它似乎过于复杂.这是一个有效的假设吗?

首先,我将以下appSettings密钥放入我的configurationProvider类返回的web.config中.

<add key="PasswordEncryptionKey" value="qTnY9lf...40 more random characters here" />

这是我的代码,可对用户进行公开身份验证,并根据输入的内容私密地检查存储的密码,并在保存时对密码进行编码.我没有显示添加或更新密码方法,因为它们使用相同的私有方法.

public bool Authenticate(string emailAddress, string password, bool setAuthCookie = false)
{
  bool isAuthenticated = false;
  var member = _memberRepository.Find(m => m.Email == emailAddress).SingleOrDefault();
  if (member != null)
  {
    if (CheckPassword(password, member.Password))
    {
      isAuthenticated = true;
      FormsAuthentication.SetAuthCookie(emailAddress, setAuthCookie);
    }
  }
  return isAuthenticated;
}

private bool CheckPassword(string providedPassword, string storedPassword)
{
  return EncodePassword(providedPassword) == storedPassword;
}

private string EncodePassword(string password)
{
  var hash = new HMACSHA1
               {
                 Key = Encoding.ASCII.GetBytes(_configurationProvider.PasswordEncryptionKey)
               };
  string encodedPassword = Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password)));
  return encodedPassword;
}

我唯一的规则是:

>密码必须在数据库中异步加密,并且不能解密.
>该方法应该是合理的,可以防止字典或其他攻击(我不是在开发FDIC受保站点,而只是在开发基本Intranet)

有了这个,有什么我想念的耀眼的东西吗?

解决方法:

我相信您的方案是足够的,但是可以稍作改进.

在我看来,您这里有一个盐腌方案的开始,并且您的app.config文件中具有EncryptionKey.但是,为了获得最佳安全性实践,通常人们为每个密码使用不同的盐,并将盐与哈希一起存储在数据库中.

class MyAuthClass {
  private const int SaltSize = 40;
  private ThreadLocal<HashAlgorithm> Hasher;

  public MyAuthClass ()
  {
    // This is 'ThreadLocal' so your methods which use this are thread-safe.
    Hasher = new ThreadLocal<HashAlgorithm>( 
      () => new HMACSHA256(Encoding.ASCII.GetBytes(_configurationProvider.PasswordEncryptionKey)
    );
  } 

  public User CreateUser(string email, string password) {
    var rng = new RNGCryptoServiceProvider();
    var pwBytes = Encoding.Unicode.GetBytes(password);
    var salt = new byte[SaltSize];
    rng.GetBytes(salt);

    var hasher = Hasher.Value;
    hasher.TransformBlock(salt, 0, SaltSize, salt, 0);
    hasher.TransformFinalBlock(pwBytes, 0, pwBytes.Length);
    var finalHash = hasher.Hash;
    return new User { UserName = email, PasswordHash = finalHash, Salt = salt };
  }

使用这样的方案,您的密码会变得更加复杂,因为如果攻击者碰巧得到了哈希,那么他还必须在蛮力攻击中猜出盐.

它与配置文件中的EncodingKey相同,但是更安全,因为每个哈希都有自己的盐.检查输入的密码类似:

  public bool IsPasswordCorrect(User u, string attempt) 
  {
    var hasher = Hasher.Value;
    var pwBytes = Encoding.Unicode.GetBytes(attempt);
    hasher.TransformBlock(u.Salt, 0, u.Salt.Length, Salt, 0);
    hasher.TransformFinalBlock(pwBytes, 0, pwBytes.Length);
    // LINQ method that checks element equality.
    return hasher.Hash.SequenceEqual(u.PasswordHash);  
  }
} // end MyAuthClass

当然,如果您宁愿将哈希存储为字符串而不是字节数组,则欢迎这样做.

只是我的2美分!

上一篇:Android: AndroidKeyStore 对数据进行签名和验证(1),精通android网络开发pdf


下一篇:36 IO流