ASP.NET domyślne hasło identyfikatora-Jak to działa i czy jest bezpieczne?

Zastanawiam się nad Hasherem hasła, który jest domyślnie zaimplementowany w UserManager , który jest dostarczany z MVC 5 i ASP.NET ramy tożsamości, czy jest wystarczająco bezpieczne? A jeśli tak, to czy mógłby mi pan wyjaśnić, jak to działa?

Interfejs IPasswordHasher wygląda tak:

public interface IPasswordHasher
{
    string HashPassword(string password);
    PasswordVerificationResult VerifyHashedPassword(string hashedPassword, 
                                                       string providedPassword);
}

Jak widzisz, nie wymaga soli, ale jest wspomniana w tym wątku: " Asp.net hashowanie hasła tożsamości " że to działa za kulisami. Więc jestem zastanawiasz się, jak to robi? A skąd pochodzi ta sól?

Moim zmartwieniem jest to, że sól jest statyczna, czyniąc ją dość niepewną.

Author: André Snede Kock, 2013-12-16

4 answers

Oto jak implementacja domyślna ( ASP.NET Framework lub ASP.NET Core ) działa. Wykorzystuje ona funkcję pochodną klucza z losową solą do wytworzenia skrótu. Sól jest dołączona jako część wyjścia KDF. Tak więc za każdym razem, gdy "hash" to samo hasło otrzymasz różne skróty. Aby zweryfikować hash, wyjście jest dzielone z powrotem na salt i resztę, a KDF jest uruchamiany ponownie na hasło z podanym salt. Jeśli wynik pasuje do reszty początkowe wyjście hash jest weryfikowany.

Hashowanie:

public static string HashPassword(string password)
{
    byte[] salt;
    byte[] buffer2;
    if (password == null)
    {
        throw new ArgumentNullException("password");
    }
    using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(password, 0x10, 0x3e8))
    {
        salt = bytes.Salt;
        buffer2 = bytes.GetBytes(0x20);
    }
    byte[] dst = new byte[0x31];
    Buffer.BlockCopy(salt, 0, dst, 1, 0x10);
    Buffer.BlockCopy(buffer2, 0, dst, 0x11, 0x20);
    return Convert.ToBase64String(dst);
}

} Weryfikacja:

public static bool VerifyHashedPassword(string hashedPassword, string password)
{
    byte[] buffer4;
    if (hashedPassword == null)
    {
        return false;
    }
    if (password == null)
    {
        throw new ArgumentNullException("password");
    }
    byte[] src = Convert.FromBase64String(hashedPassword);
    if ((src.Length != 0x31) || (src[0] != 0))
    {
        return false;
    }
    byte[] dst = new byte[0x10];
    Buffer.BlockCopy(src, 1, dst, 0, 0x10);
    byte[] buffer3 = new byte[0x20];
    Buffer.BlockCopy(src, 0x11, buffer3, 0, 0x20);
    using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(password, dst, 0x3e8))
    {
        buffer4 = bytes.GetBytes(0x20);
    }
    return ByteArraysEqual(buffer3, buffer4);
}
 248
Author: Andrew Savinykh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-22 18:26:44

Ponieważ te dni ASP.NET jest open source, można go znaleźć na GitHub: AspNet.Identity 3.0 i AspNet.Tożsamość 2.0 .

Z komentarzy:

/* =======================
 * HASHED PASSWORD FORMATS
 * =======================
 * 
 * Version 2:
 * PBKDF2 with HMAC-SHA1, 128-bit salt, 256-bit subkey, 1000 iterations.
 * (See also: SDL crypto guidelines v5.1, Part III)
 * Format: { 0x00, salt, subkey }
 *
 * Version 3:
 * PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations.
 * Format: { 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey }
 * (All UInt32s are stored big-endian.)
 */
 46
Author: Knelis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-07 07:57:39

Rozumiem przyjętą odpowiedź i zagłosowałem na nią, ale pomyślałem, że wrzucę tutaj odpowiedź laika...

Tworzenie hasha

  1. sól jest generowana losowo za pomocą funkcji Rfc2898DeriveBytes który generuje hash i salt. Dane wejściowe do Rfc2898DeriveBytes to hasło, Rozmiar soli do wygenerowania oraz liczba iteracji hashujących do wykonaj. https://msdn.microsoft.com/en-us/library/h83s4e12 (v=vs.110). aspx
  2. Sól i hash są następnie zmiażdżone razem(sól najpierw przez hash) i zakodowany jako ciąg znaków (tak więc sól jest zakodowana w hash). Ten zakodowany hash (który zawiera sól i hash) jest następnie przechowywany (zazwyczaj) w bazie danych przed użytkownikiem.

Sprawdzanie hasła pod hash

Aby sprawdzić hasło wprowadzone przez użytkownika.

  1. sól jest wydobyte z przechowywanego hashowanego hasła.
  2. salt jest używany do hashowania hasła wejściowego użytkownika przy użyciu przeciążenia Rfc2898DeriveBytes , które pobiera salt zamiast generowania. https://msdn.microsoft.com/en-us/library/yx129kfs (v=vs.110). aspx
  3. przechowywany hash i hash testowy są następnie porównywane.

Hash

Pod pokrywami hash jest generowany za pomocą funkcji skrótu SHA1 ( https://en.wikipedia.org/wiki/SHA-1 ). Ta funkcja jest iteracyjnie wywoływana 1000 razy (w domyślnej implementacji tożsamości)

Dlaczego jest to bezpieczne

  • losowe sole oznacza, że atakujący nie może użyć wstępnie Wygenerowanej tabeli hashów, aby próbować łamać hasła. Będą musieli wygenerować stół hash na każdą sól. (Zakładając, że haker również skompromitował Twoją sól) {]}
  • Jeśli 2 hasła są identyczne to mają różne hasze. (czyli napastnicy nie mogą wywnioskować "pospolitego" hasła)
  • Iteratywne wywołanie SHA1 1000 razy oznacza, że atakujący również musi to zrobić. Chodzi o to, że jeśli nie mają czasu na superkomputerze nie będą mieli wystarczająco dużo zasobów, aby Wymuś hasło z hasha. To znacznie spowolni czas generowania tabeli hash dla danej soli.
 35
Author: Nattrass,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-06 08:36:22

Dla takich jak ja, którzy są nowicjuszami, oto kod z const i rzeczywisty sposób na porównanie bajtów []. otrzymałem cały ten kod ze stackoverflow, ale zdefiniowane const, więc wartości można zmienić, a także

// 24 = 192 bits
    private const int SaltByteSize = 24;
    private const int HashByteSize = 24;
    private const int HasingIterationsCount = 10101;


    public static string HashPassword(string password)
    {
        // http://stackoverflow.com/questions/19957176/asp-net-identity-password-hashing

        byte[] salt;
        byte[] buffer2;
        if (password == null)
        {
            throw new ArgumentNullException("password");
        }
        using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(password, SaltByteSize, HasingIterationsCount))
        {
            salt = bytes.Salt;
            buffer2 = bytes.GetBytes(HashByteSize);
        }
        byte[] dst = new byte[(SaltByteSize + HashByteSize) + 1];
        Buffer.BlockCopy(salt, 0, dst, 1, SaltByteSize);
        Buffer.BlockCopy(buffer2, 0, dst, SaltByteSize + 1, HashByteSize);
        return Convert.ToBase64String(dst);
    }

    public static bool VerifyHashedPassword(string hashedPassword, string password)
    {
        byte[] _passwordHashBytes;

        int _arrayLen = (SaltByteSize + HashByteSize) + 1;

        if (hashedPassword == null)
        {
            return false;
        }

        if (password == null)
        {
            throw new ArgumentNullException("password");
        }

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

        if ((src.Length != _arrayLen) || (src[0] != 0))
        {
            return false;
        }

        byte[] _currentSaltBytes = new byte[SaltByteSize];
        Buffer.BlockCopy(src, 1, _currentSaltBytes, 0, SaltByteSize);

        byte[] _currentHashBytes = new byte[HashByteSize];
        Buffer.BlockCopy(src, SaltByteSize + 1, _currentHashBytes, 0, HashByteSize);

        using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(password, _currentSaltBytes, HasingIterationsCount))
        {
            _passwordHashBytes = bytes.GetBytes(SaltByteSize);
        }

        return AreHashesEqual(_currentHashBytes, _passwordHashBytes);

    }

    private static bool AreHashesEqual(byte[] firstHash, byte[] secondHash)
    {
        int _minHashLength = firstHash.Length <= secondHash.Length ? firstHash.Length : secondHash.Length;
        var xor = firstHash.Length ^ secondHash.Length;
        for (int i = 0; i < _minHashLength; i++)
            xor |= firstHash[i] ^ secondHash[i];
        return 0 == xor;
    }

W niestandardowej aplikacji UserManager ustawiasz właściwość PasswordHasher nazwę klasy, która zawiera powyższy kod.

 8
Author: kfrosty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-12 20:51:43