Wat is het standaard hash-algoritme dat het ASP.NET-lidmaatschap gebruikt?

Wat is het standaard hash-algoritme dat het ASP.NET-lidmaatschap gebruikt? En hoe kan ik dit veranderen?


Antwoord 1, autoriteit 100%

BEWERKEN: gebruik de lidmaatschapsprovider niet zoals hij is, omdat deze vreselijk ontoereikend is in termen van het beschermen van gebruikerswachtwoorden

In het licht van het feit dat googlen op “lidmaatschap provider hashing-algoritme”dit antwoord opduikt als het eerste resultaat, en het evangelie dat zal worden afgeleid, betaamt het mij om mensen te waarschuwen voor het gebruik van de Membership Provider op deze manier en het gebruik van hashes zoals SHA-1, MD5 enz. om wachtwoorden in databases te verdoezelen.

tl;dr

Gebruik een sleutelafleidingsfunctie zoals bcrypt, scrypt of (als je FIPS-compliance nodig hebt) ) PBKDF2met een werkfactor die voldoende is om de hashtijd voor een enkel wachtwoord zo dicht mogelijk bij 1000 ms of meer te brengen.

Hashes zijn tegenwoordig gemakkelijk te brute kracht met voldoende voorbeelden van datalekken in de recente geschiedenis. Om te voorkomen dat de wachtwoorden van uw gebruiker bij de volgende hack in de pastebin belanden, moet u ervoor zorgen dat wachtwoorden worden gehasht met een functie die voldoende lang duurt om te berekenen!

Probeer in plaats van Membership Provider, IdentityRebootof de nieuwere implementaties van Microsoft waar Troy Hunt het over heeftop de tenminste.

Het is ook interessant dat ik in dezelfde Google-resultaten als hierboven vermeld een tutorial die mensen laat zien hoe gemakkelijk het isom deze wachtwoord-hashes bruut te forceren met behulp van populaire tools zoals JtR of Hashcat. Op een aangepaste GPU-rig kan SHA1 worden gekraakt met een onthutsende snelheid van 48867 miljoen hashes per seconde!Met een gratis woordenboek zoals rockyou of iets dergelijks, een gemotiveerd persoon met je database zal zeer snel de meeste van uw gebruikerswachtwoorden hebben. Als ontwikkelaar is het uw ethische verantwoordelijkheid om te doen wat nodig is om de veiligheid van de wachtwoorden van uw gebruikers te beschermen.


De standaard hashing is SHA1, maar ze salten het ook en base64 het:

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

Als je meer wilt weten over hoe je dit kunt wijzigen, moet ik dat nog weten (tenzij je een aangepaste provider gebruikt, zie hieronder), maar SHA-1 is voorlopig redelijk goed. Als je het wilt omkeren of dit wilt opzoeken, hebben deze jongens daar wat aan gewerkt: http: //forums.asp.net/p/1336657/2899172.aspx

Deze SO-vraag helpt bij het omkeren of dupliceren van deze techniek als dat nodig is. ASP.NET-lidmaatschap en gebruikerswachtwoord-hashing in Ruby opnieuw implementeren

Als u een aangepaste provider maakt, kunt u uw hash- en encryptie-algoritmen en -methoden maken.

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


Antwoord 2, autoriteit 78%

Het bovenstaande antwoord van Ryan Christensenis niet compleet. Het gedeelte waar het het zout omzet in een byte[] is niet correct.

Dit is een werkend voorbeeld dat ik heb geïmplementeerd in een oplossing voor een klant:

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

Antwoord 3, autoriteit 57%

Het standaard hash-algoritmetype is SHA1. U kunt dit op twee manieren wijzigen.

1) Als u met IIS 7 werkt, kunt u dit bijwerken met behulp van de “Machine Key”-configuratie (hieronder weergegeven). Hiermee kunt u de coderingsmethode kiezen uit een lijst met beschikbare opties en de sleutels of de opties voor het genereren van sleutels specificeren.

2) Als u met IIS 6 werkt, kunt u het type hash-algoritme wijzigen met behulp van het lidmaatschapselement in het bestand web.config:

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

Volgens de documentatie de tekenreekswaarde van het hashAlgorithmType-attribuut kan elk van de opgegeven .Net-hash-algoritmetypen zijn. Een beetje graven leert dat de geldige waarden voor ASP.Net 2, 3 en 3.5 zijn MD5, RIPEMD160, SHA1, SHA256, SHA384, SHA512. Het belangrijkste hier is dat al deze klassen erven van HashAlgorithm.

De waarde van het kenmerk hashAlgorithmTypekan ook een vermelding zijn van het cryptoNameMapping-element in het bestand machine.config. U kunt dit gebruiken als u een hash-algoritme van een derde partij nodig heeft. Het bestand machine.config is doorgaans te vinden in C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIGals u ASP.Net 2 of hoger gebruikt. U kunt hiermeer lezen over het instellen van deze waarden.


Antwoord 4, autoriteit 53%

Het standaard hash-algoritme is gewijzigd in HMACSHA256 in het .NET 4.0 Framework.

Houd er rekening mee dat HMAC SHA-256, in tegenstelling tot SHA-1, een ingetoetste hash is. Als je hashes zich niet-deterministisch gedragen, heb je waarschijnlijk geen sleutel ingesteld, waardoor deze gedwongen is een willekeurige sleutel te gebruiken. Iets wat lijkt op het volgende zou de boosdoener zijn (wat ik net een uur heb besteed aan het uitzoeken :p ).

HashAlgorithm.Create(Membership.HashAlgorithmType)

Als je wilt dat het werkt met een bestaande provider, kun je het terugzetten naar de vorige standaardinstellingen met behulp van deze handleiding.


Antwoord 5, autoriteit 6%

Er is één correctie in het hash-algoritme die u moet gebruiken:

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

in plaats van

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

Lees artikel http://svakodnevnica.com.ba/index.php?option=com_kunena&func=view&catid=4&id=4&Itemid=5&lang=nl#6


Antwoord 6, autoriteit 2%

Laten we de antwoorden op deze vraag bespreken die veilig en beproefd zijn:

  1. ZeteticSlechts twee regels code en klaar!Het hash-algoritme PBKDF2 is veel beter dan het hebben van SHA1 of SHA256-SHA512 enz. De nieuwste algoritmen zoals PBKDF2, SCRYPT of ARGON2 zijn toonaangevend als het op hashen aankomt. Maar het gebruik van PBKDF2 is in dit geval handig omdat het door .NET wordt geïmplementeerd in de klasse Rfc2898DeriveBytes. Het gebruik van deze bibliotheek was tot nu toe geweldig, maar er zijn enkele kleine problemen, zoals:

    a. Zetetic gebruikt standaard 5000 iteraties. Aanpasbaar als u Pbkdf2Hash256K

    gebruikt

    b. Zetetic gebruik Rfc2898DeriveBytesen Rfc2898DeriveBytesis om de een of andere reden gebaseerd op HMACSHA1en kan niet worden aangepast.

  2. Goed nieuws! Ik heb Rfc2898DeriveBytesaangepast om HMACSHA512te gebruiken met 128.000 iteraties, zodat SQLMembershipProvider PBKDF2 kan gebruiken die tot nu toe niet beschikbaar was. Om dit te bereiken heb ik Zetetic’scode gecombineerd met mijn implementatie van Rfc2898DeriveByteszoals hieronder weergegeven:

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

Doe dit na het maken van deze les:

  • Voeg de volgende regel toe aan de Application_Start-gebeurtenis van Global.asax of het respectieve opstartbestand van uw project:

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

  • En verander web.config als:

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

Referenties voor het construeren van dit antwoord zijn ontleend aan:


Antwoord 7

Ik voeg een fragment toe met de code zoals in het antwoord van Rawbert hierboven in F#

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)

Dit is werkende code van een actieve applicatie

Other episodes