ASP.NET成员资格使用的默认哈希算法是什么?

时间:2009-07-16 12:51:40

标签: asp.net authentication hash membership

ASP.NET成员资格使用的默认哈希算法是什么?我该如何改变呢?

7 个答案:

答案 0 :(得分:46)

编辑:不要按原样使用会员提供商,因为它在保护用户密码方面严重不足

鉴于googling "membership provider hashing algorithm"将这个答案作为第一个结果,以及将被推断出来的福音这一事实,我应该警告人们使用像这样的成员资格提供者并使用像SHA这样的哈希值1,MD5等模糊数据库中的密码。

TL;博士

Use a key-derivation function like bcrypt, scrypt or (if you need FIPS compliance) PBKDF2,其工作因素足以使单个密码的散列时间达到1000毫秒或更长。

近来历史上有大量数据泄露的例子,哈希很容易蛮力。为了防止用户的密码在下一次黑客攻击中结束使用pastebin,请确保使用足够长的时间进行计算的函数对密码进行哈希处理!

取代会员提供商,至少尝试IdentityRebootnewer implementations from Microsoft that Troy Hunt talks about

同样有趣的是,在上面提到的相同谷歌搜索结果中,我发现tutorial showing folks preciously how easy it is使用JtR或Hashcat等流行工具来强制使用这些密码哈希值。在自定义GPU装备上,SHA1可以在staggering rate of 48867 million hashes per second!处破解。使用免费字典rockyou or the like,有数据库的有动力的人很快就会拥有大部分用户密码。作为开发人员,做出保护用户密码安全所必需的道德责任是您的道德责任。


默认散列是SHA1,但是它们也将它加盐并使它为base64:

public string EncodePassword(string pass, string salt)
{
    byte[] bytes = Encoding.Unicode.GetBytes(pass);
    byte[] src = Encoding.Unicode.GetBytes(salt);
    byte[] dst = new byte[src.Length + bytes.Length];
    Buffer.BlockCopy(src, 0, dst, 0, src.Length);
    Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
    HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");
    byte[] inArray = algorithm.ComputeHash(dst);
    return Convert.ToBase64String(inArray);
}

如果您想了解更多关于如何更改它的信息,我仍然需要了解(除非使用自定义提供程序,请参阅下文)但是SHA-1现在非常好用。如果你想要反转它或从中查找这些人做了一些工作:http://forums.asp.net/p/1336657/2899172.aspx

如果可能需要,这个SO问题将有助于扭转或复制这种技术。 Reimplement ASP.NET Membership and User Password Hashing in Ruby

如果您正在制作自定义提供程序,则可以创建散列和加密算法和方法。

private byte[] ConvertPasswordForStorage(string Password)
      {
         System.Text.UnicodeEncoding ue = 
      new System.Text.UnicodeEncoding();
         byte[] uePassword = ue.GetBytes(Password);
         byte[] RetVal = null;
         switch (_PasswordFormat)
         {
            case MembershipPasswordFormat.Clear:
               RetVal = uePassword;
               break;
            case MembershipPasswordFormat.Hashed:

               HMACSHA1 SHA1KeyedHasher = new HMACSHA1();
               SHA1KeyedHasher.Key = _ValidationKey;
               RetVal = SHA1KeyedHasher.ComputeHash(uePassword);
               break;
            case MembershipPasswordFormat.Encrypted:
               TripleDESCryptoServiceProvider tripleDes = new 
       TripleDESCryptoServiceProvider();
               tripleDes.Key = _DecryptionKey;
               tripleDes.IV = new byte[8];
               MemoryStream mStreamEnc = new MemoryStream();
               CryptoStream cryptoStream = new CryptoStream(mStreamEnc, 
        tripleDes.CreateEncryptor(), 
      CryptoStreamMode.Write);

               cryptoStream.Write(uePassword, 0, uePassword.Length);
               cryptoStream.FlushFinalBlock();
               RetVal = mStreamEnc.ToArray();
               cryptoStream.Close();
               break;

         }
         return RetVal;
      }

private string GetHumanReadablePassword(byte[] StoredPassword)
      {
         System.Text.UnicodeEncoding ue = new System.Text.UnicodeEncoding();
         string RetVal = null;
         switch (_PasswordFormat)
         {
            case MembershipPasswordFormat.Clear:
               RetVal = ue.GetString(StoredPassword);
               break;
            case MembershipPasswordFormat.Hashed:
               throw new ApplicationException(
        "Password cannot be recovered from a hashed format");

            case MembershipPasswordFormat.Encrypted:
               TripleDESCryptoServiceProvider tripleDes = 
        new TripleDESCryptoServiceProvider();
               tripleDes.Key = _DecryptionKey;
               tripleDes.IV = new byte[8];
               CryptoStream cryptoStream = 
        new CryptoStream(new MemoryStream(StoredPassword), 
      tripleDes.CreateDecryptor(), CryptoStreamMode.Read);
               MemoryStream msPasswordDec = new MemoryStream();
               int BytesRead = 0;
               byte[] Buffer = new byte[32];
               while ((BytesRead = cryptoStream.Read(Buffer, 0, 32)) > 0)
               {
                  msPasswordDec.Write(Buffer, 0, BytesRead);

               }
               cryptoStream.Close();

               RetVal = ue.GetString(msPasswordDec.ToArray());
               msPasswordDec.Close();
               break;
         }
         return RetVal;
      }

http://msdn.microsoft.com/en-us/library/aa479048.aspx

答案 1 :(得分:30)

above answer by Ryan Christensen未完成。它将盐转换为byte []的部分不正确。

这是我在客户端解决方案中实现的一个工作示例:

public string Hash(string value, string salt)
    {
        byte[] bytes = Encoding.Unicode.GetBytes(value);
        byte[] src = Convert.FromBase64String(salt);
        byte[] dst = new byte[src.Length + bytes.Length];
        Buffer.BlockCopy(src, 0, dst, 0, src.Length);
        Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
        HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");
        byte[] inArray = algorithm.ComputeHash(dst);
        return Convert.ToBase64String(inArray);
    }

答案 2 :(得分:27)

默认的哈希算法类型是SHA1。有两种方法可以改变它。

1)如果您使用的是IIS 7,可以使用“机器密钥”配置对其进行更新(如下所示)。这允许您从可用选项列表中选择加密方法,并指定键或密钥生成选项。

Machine Key configuration page from IIS 7 administration tool

2)如果您正在使用IIS 6,则可以使用web.config文件中的membership元素更改哈希算法类型:

<membership
    defaultProvider="provider name"
    userIsOnlineTimeWindow="number of minutes"
    hashAlgorithmType="SHA1">
    <providers>...</providers>
</membership>

根据文档,hashAlgorithmType属性的字符串值可以是任何提供的.Net哈希算法类型。一些挖掘表明,ASP.Net 2,3和3.5的有效值为MD5RIPEMD160SHA1SHA256SHA384,{ {1}}。这里重要的部分是所有这些类都继承自SHA512

hashAlgorithmType属性的值也可以是machine.config文件中cryptoNameMapping元素的条目。如果您需要第三方哈希算法,则可以使用此方法。如果您使用的是ASP.Net 2或更高版本,则通常可以在HashAlgorithm中找到machine.config文件。您可以详细了解如何设置这些值here

答案 3 :(得分:26)

默认哈希算法在.NET 4.0 Framework中更改为HMACSHA256。

请注意,与SHA-1不同,HMAC SHA-256是一个键控哈希。如果您的哈希行为不确定,您可能没有设置密钥,强制它使用随机密钥。类似于以下的东西将是罪魁祸首(这是我花了一个小时搞清楚:p)。

HashAlgorithm.Create(Membership.HashAlgorithmType)

如果您希望将其与现有提供商配合使用,则可以使用this guide将其还原为以前的默认设置。

答案 4 :(得分:2)

哈希算法中有一个更正,您必须使用:

byte[] src = Convert.FromBase64String(salt);

而不是

byte[] src = Encoding.Unicode.GetBytes(salt);

阅读文章http://svakodnevnica.com.ba/index.php?option=com_kunena&func=view&catid=4&id=4&Itemid=5&lang=en#6

答案 5 :(得分:1)

让我们讨论这个问题的答案,这些答案是安全且经过时间检验的:

  1. Zetetic 仅两行代码就完成了!散列算法PBKDF2比拥有SHA1或SHA256-SHA512等要好得多。最新的算法如PBKDF2,SCRYPT或ARGON2在散列方面是领导者。但是在这种情况下使用PBKDF2是有用的,因为它是由.NET在ConfigureAwait(false)类中实现的。到现在为止,使用该库还是很棒的,但是存在一些小问题,例如:

    a。 Zetetic默认使用5000次迭代。如果您使用java.sql.SQLException: No suitable driver found for jdbc:mysql//localhost:3306/employee at java.sql.DriverManager.getConnection(DriverManager.java:700) at java.sql.DriverManager.getConnection(DriverManager.java:258) at jdbcdemo.Driver.main(Driver.java:14

    ,则可以自定义

    b。出于某些原因,Zetetic使用Rfc2898DeriveBytesPbkdf2Hash256K基于Rfc2898DeriveBytes,因此无法自定义。

  2. 好消息!我已自定义Rfc2898DeriveBytes以使用HMACSHA1进行128,000次迭代,以便SQLMembershipProvider可以使用PBKDF2,到目前为止尚不可用。为此,我将Zetetic's代码与Rfc2898DeriveBytes的实现结合在一起,如下所示:

HMACSHA512

Rfc2898DeriveBytes

创建此类后,请执行以下操作:

  • 将以下行添加到Global.asax的Application_Start事件或项目的相应启动文件中:

    using System.Security.Cryptography;

  • 并将web.config更改为:

    namespace custom.hashing.keyderivation { /// <summary> /// This derived class of PBKDF2Hash provided necessary capabilities to SQLMembershipProvider in order to hash passwords in PBKDF2 way with 128,000 iterations. /// </summary> public class PBKDF2Hash : KeyedHashAlgorithm { private const int kHashBytes = 64; private System.IO.MemoryStream _ms; public int WorkFactor { get; set; } public PBKDF2Hash() : base() { this.WorkFactor = 128000; this.Key = new byte[32]; // 32 Bytes will give us 256 bits. using (var rngCsp = new RNGCryptoServiceProvider()) { // Fill the array with cryptographically secure random bytes. rngCsp.GetBytes(this.Key); } } /// <summary> /// Hash size in bits /// </summary> public override int HashSize { get { return kHashBytes * 8; } } protected override void HashCore(byte[] array, int ibStart, int cbSize) { (_ms = _ms ?? new System.IO.MemoryStream()).Write(array, ibStart, cbSize); } protected override byte[] HashFinal() { if (this.Key == null || this.Key.Length == 0) { throw new CryptographicException("Missing KeyedAlgorithm key"); } _ms.Flush(); var arr = _ms.ToArray(); _ms = null; using (var hmac = new HMACSHA512()) { return new MyRfc2898DeriveBytes(arr, this.Key, this.WorkFactor, hmac).GetBytes(kHashBytes); } } public override void Initialize() { _ms = null; } } // ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== // <OWNER>Microsoft</OWNER> // // // Rfc2898DeriveBytes.cs // // This implementation follows RFC 2898 recommendations. See http://www.ietf.org/rfc/Rfc2898.txt /// <summary> /// Microsoft has implemented PBKDF2 but with HMACSHA1. We are customizing this class to use HMACSHA512 in hashing process. /// </summary> public class MyRfc2898DeriveBytes : DeriveBytes { private byte[] m_buffer; private byte[] m_salt; private HMAC m_hmac; // The pseudo-random generator function used in PBKDF2 private uint m_iterations; private uint m_block; private int m_startIndex; private int m_endIndex; private int m_blockSize; // // public constructors // // This method needs to be safe critical, because in debug builds the C# compiler will include null // initialization of the _safeProvHandle field in the method. Since SafeProvHandle is critical, a // transparent reference triggers an error using PasswordDeriveBytes. [SecuritySafeCritical] public MyRfc2898DeriveBytes(byte[] password, byte[] salt, int iterations, HMAC hmac) { Salt = salt; IterationCount = iterations; hmac.Key = password; m_hmac = hmac; // m_blockSize is in bytes, HashSize is in bits. m_blockSize = hmac.HashSize >> 3; Initialize(); } // // public properties // public int IterationCount { get { return (int)m_iterations; } set { if (value <= 0) throw new ArgumentOutOfRangeException("value", "Error: Iteration count is zero or less"); m_iterations = (uint)value; Initialize(); } } public byte[] Salt { get { return (byte[])m_salt.Clone(); } set { if (value == null) throw new ArgumentNullException("value"); if (value.Length < 8) throw new ArgumentException("Error: Salt size is less than 8"); m_salt = (byte[])value.Clone(); Initialize(); } } // // public methods // public override byte[] GetBytes(int cb) { if (cb <= 0) { throw new ArgumentOutOfRangeException("cb", "Error: Hash size is zero or less"); } Contract.Assert(m_blockSize > 0); byte[] password = new byte[cb]; int offset = 0; int size = m_endIndex - m_startIndex; if (size > 0) { if (cb >= size) { Buffer.BlockCopy(m_buffer, m_startIndex, password, 0, size); m_startIndex = m_endIndex = 0; offset += size; } else { Buffer.BlockCopy(m_buffer, m_startIndex, password, 0, cb); m_startIndex += cb; return password; } } Contract.Assert(m_startIndex == 0 && m_endIndex == 0, "Invalid start or end index in the internal buffer."); while (offset < cb) { byte[] T_block = Func(); int remainder = cb - offset; if (remainder > m_blockSize) { Buffer.BlockCopy(T_block, 0, password, offset, m_blockSize); offset += m_blockSize; } else { Buffer.BlockCopy(T_block, 0, password, offset, remainder); offset += remainder; Buffer.BlockCopy(T_block, remainder, m_buffer, m_startIndex, m_blockSize - remainder); m_endIndex += (m_blockSize - remainder); return password; } } return password; } public override void Reset() { Initialize(); } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) { if (m_hmac != null) { ((IDisposable)m_hmac).Dispose(); } if (m_buffer != null) { Array.Clear(m_buffer, 0, m_buffer.Length); } if (m_salt != null) { Array.Clear(m_salt, 0, m_salt.Length); } } } private void Initialize() { if (m_buffer != null) Array.Clear(m_buffer, 0, m_buffer.Length); m_buffer = new byte[m_blockSize]; m_block = 1; m_startIndex = m_endIndex = 0; } internal static byte[] GetBytesFromInt(uint i) { return unchecked(new byte[] { (byte)(i >> 24), (byte)(i >> 16), (byte)(i >> 8), (byte)i }); } // This function is defined as follow : // Func (S, i) = HMAC(S || i) | HMAC2(S || i) | ... | HMAC(iterations) (S || i) // where i is the block number. private byte[] Func() { byte[] INT_block = GetBytesFromInt(m_block); m_hmac.TransformBlock(m_salt, 0, m_salt.Length, null, 0); m_hmac.TransformBlock(INT_block, 0, INT_block.Length, null, 0); m_hmac.TransformFinalBlock(new byte[0], 0, 0); byte[] temp = m_hmac.Hash; m_hmac.Initialize(); byte[] ret = temp; for (int i = 2; i <= m_iterations; i++) { m_hmac.TransformBlock(temp, 0, temp.Length, null, 0); m_hmac.TransformFinalBlock(new byte[0], 0, 0); temp = m_hmac.Hash; for (int j = 0; j < m_blockSize; j++) { ret[j] ^= temp[j]; } m_hmac.Initialize(); } // increment the block count. if (m_block == uint.MaxValue) { throw new InvalidOperationException("Derived key too long."); } m_block++; return ret; } }

构造此答案的参考来自:

答案 6 :(得分:0)

我附上一个显示代码的代码片段,如上面在F#中的Rawbert答案

open System
open System.Security.Cryptography
open System.Text

module PasswordHelper =
    let EncodePassword(pass : string, salt : string) =
        let bytes = Encoding.Unicode.GetBytes(pass)
        let src = Convert.FromBase64String(salt)
        let dst : byte array = Array.zeroCreate (src.Length + bytes.Length)
        Buffer.BlockCopy(src, 0, dst, 0, src.Length)
        Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length)
        let algorithm = HashAlgorithm.Create("SHA1")
        let inArray = algorithm.ComputeHash(dst)
        Convert.ToBase64String(inArray)

这是来自活动应用程序的工作代码