bestsource

ASP.NET 멤버쉽에서 사용하는 기본 해시 알고리즘은 무엇입니까?

bestsource 2023. 6. 18. 16:11
반응형

ASP.NET 멤버쉽에서 사용하는 기본 해시 알고리즘은 무엇입니까?

ASP.NET 멤버쉽에서 사용하는 기본 해시 알고리즘은 무엇입니까?그리고 어떻게 바꿀 수 있습니까?

편집: 멤버십 제공자는 사용자의 비밀번호 보호 측면에서 매우 부적절하므로 그대로 사용하지 마십시오.

구글링 "멤버십 제공자 해싱 알고리즘"이 이 답을 첫 번째 결과로 제시하고 추론될 복음에 비추어 볼 때, 회원권 제공자를 이렇게 사용하고 SHA-1, MD5와 같은 해시를 사용하여 데이터베이스의 암호를 난독화하는 것에 대해 사람들에게 경고해야 합니다.

tl;dr

단일 암호가 1,000ms 또는 그 이상이 되도록 해싱 시간을 필요로 하는 작업 요소가 있는 bcrypt, scrypt 또는 (FIPS 준수가 필요한 경우) PBKDF2와 같은 키 파생 기능을 사용합니다.

최근의 역사에서 데이터 침해 사례가 풍부한 요즘 해시는 무차별적인 공격을 가하기 쉽습니다.다음 해킹에서 사용자의 비밀번호가 페이스트빈으로 끝나는 것을 방지하려면, 비밀번호가 계산에 충분히 오랜 시간이 걸리는 기능으로 해시되도록 하십시오!

Membership Provider 대신 IdentityReboot 또는 Troy Hunt가 언급한 Microsoft의 최신 구현사용해 보십시오.

또한 위에서 언급한 동일한 구글 결과에서 JtR 또는 Hashcat과 같은 인기 있는 도구를 사용하여 이러한 암호 해시를 브루트하는 것이 얼마나 쉬운지를 사람들에게 귀중하게 보여주는 튜토리얼을 발견했다는 것이 흥미롭습니다.맞춤형 GPU 리그에서 SHA1은 초당 4억 867만 해시놀라운 속도로 균열될 수 있습니다!Rockyou 등과 같은 무료 사전을 사용하면 데이터베이스를 사용하는 동기 부여된 사용자가 대부분의 사용자 암호를 매우 빠르게 가질 수 있습니다.개발자로서 사용자 암호의 보안을 보호하기 위해 필요한 작업을 수행하는 것은 윤리적 책임입니다.


기본 해싱은 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 질문은 필요한 경우 이 기술을 반전시키거나 복제하는 데 도움이 됩니다.Ruby에서 ASP.NET 멤버십 및 사용자 암호 해싱 재구현

사용자 지정 공급자를 만드는 경우 해싱 및 암호화 알고리즘 및 방법을 만들 수 있습니다.

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

위의 Ryan Christensen의 답변은 완전하지 않습니다.소금을 바이트[]로 변환하는 부분이 잘못되었습니다.

다음은 고객을 위한 솔루션에 구현한 작업 예제입니다.

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);
    }

기본 해시 알고리즘 유형은 SHA1입니다.이를 변경할 수 있는 두 가지 방법이 있습니다.

IIS 7로 작업하는 경우 "기계 키" 구성(아래 표시)을 사용하여 이를 업데이트할 수 있습니다.이렇게 하면 사용 가능한 옵션 목록에서 암호화 방법을 선택하고 키 또는 키 생성 옵션을 지정할 수 있습니다.

Machine Key configuration page from IIS 7 administration tool

IIS 6으로 작업하는 경우 web.config 파일의 membership 요소를 사용하여 해시 알고리즘 유형을 변경할 수 있습니다.

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

설명서에 따르면 hashAlgorithm의 문자열 값은형식 특성은 제공된 특성 중 하나일 수 있습니다.해시 알고리즘 유형입니다.약간의 발굴을 통해 ASP.Net 2, 3 및 3.5의 유효한 값이MD5,RIPEMD160,SHA1,SHA256,SHA384,SHA512여기서 중요한 부분은 이 모든 클래스가 다음으로부터 상속된다는 것입니다.HashAlgorithm.

해시 알고리즘의 값유형 특성은 machine.config 파일에 있는 cryptoNameMapping 요소의 항목일 수도 있습니다.타사 해싱 알고리즘이 필요한 경우 이를 사용할 수 있습니다.machine.config 파일은 일반적으로 다음에서 찾을 수 있습니다.C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIGASP.Net 2 이상을 사용하는 경우. 값 설정에 대한 자세한 내용은 여기를 참조하십시오.

.NET 4.0 프레임워크에서 기본 해시 알고리즘이 HMACSHA256으로 변경되었습니다.

SHA-1과 달리 HMAC SHA-256은 키 해시입니다.해시가 비결정론적으로 동작하는 경우 키를 설정하지 않아 임의의 키를 사용하도록 강제할 수 있습니다.다음과 유사한 것이 범인일 것입니다(이것이 바로 제가 방금 파악한 것입니다:p).

HashAlgorithm.Create(Membership.HashAlgorithmType)

기존 공급자와 함께 작동하도록 하려면 이 안내서를 사용하여 이전 기본값으로 되돌릴 수 있습니다.

해싱 알고리즘에는 다음과 같은 수정 사항이 있습니다.

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

안전하고 시간이 오래 걸리는 이 질문에 대한 답변에 대해 논의해 보겠습니다.

  1. Zetetic 코드 이면 끝!해싱 알고리즘 PBKDF2는 SHA1이나 SHA256-SHA512 등을 사용하는 것보다 훨씬 좋습니다.PBKDF2, SCRYPT 또는 ARGON2와 같은 최신 알고리즘은 해시에 관한 한 선두입니다.그러나 PBKDF2를 사용하는 것은 .NET에 의해 구현되기 때문에 이 경우에 유용합니다.Rfc2898DeriveBytes수업. 지금까지 이 라이브러리를 사용하는 것은 훌륭했지만 다음과 같은 몇 가지 사소한 문제가 있습니다.

    Zetetic은 기본적으로 5000회 반복을 사용합니다.사용자 정의 가능(사용자 정의 가능)Pbkdf2Hash256K

    제틱 사용Rfc2898DeriveBytes그리고.Rfc2898DeriveBytes기반HMACSHA1사용자 지정할 수 없습니다.

  2. 좋은 소식!사용자 지정Rfc2898DeriveBytes사용할HMACSHA512SQL Membership Provider가 지금까지 사용할 수 없었던 PBKDF2를 사용할 수 있도록 128,000회 반복합니다.이를 위해 Zetetic의 코드를 제가 구현한 것과 결합했습니다.Rfc2898DeriveBytes아래와 같이:

using System.Security.Cryptography;

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;
    }
}

이 클래스를 만든 후 다음 작업을 수행합니다.

  • Global.asax 또는 프로젝트의 해당 시작 파일의 Application_Start 이벤트에 다음 행을 추가합니다.

    System.Security.Cryptography.CryptoConfig.AddAlgorithm(typeof(custom.hashing.keyderivation.PBKDF2Hash), "PBKDF2Hash_HB");

  • web.config를 다음과 같이 변경합니다.

    <membership defaultProvider="sitecore" hashAlgorithmType="PBKDF2Hash_HB">

이 답변을 작성하기 위한 참고 자료는 다음에서 가져옵니다.

위 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)

활성 응용 프로그램의 작업 코드입니다.

언급URL : https://stackoverflow.com/questions/1137368/what-is-default-hash-algorithm-that-asp-net-membership-uses

반응형