ASP.NET identité par défaut mot de passe Hasher, comment cela fonctionne - t-il et est-il sécurisé?

je me demande si le Hasher de mot de passe qui est par défaut mis en œuvre dans le UserManager qui vient avec MVC 5 et ASP.NET le cadre D'identité, est-il assez sûr? Et si oui, si vous pouviez m'expliquer comment cela fonctionne?

ipasswordhasher interface ressemble à ceci:

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

comme vous pouvez le voir, il ne prend pas un sel, mais il est mentionné dans ce fil: " Asp.net identification hachage de mot de passe " qu'il ne enfait sel derrière les coulisses. Donc, je me demande comment fait-il cela? Et d'où vient ce sel provient?

mon souci est que le sel est statique, le rendant assez peu sûr.

119
demandé sur Community 2013-12-17 02:08:42

4 réponses

Voici comment fonctionne la implementation par défaut . Il utilise une fonction de dérivation clé avec du sel aléatoire pour produire le hash. Le sel est inclus dans la production du KDF. Ainsi, chaque fois que vous" hachez " le même mot de passe vous obtiendrez des hachures différentes. Pour vérifier le hachage, la sortie est renvoyée au sel et au reste, et le KDF est lancé de nouveau sur le mot de passe avec le sel spécifié. Si le résultat correspond au reste de la sortie initiale le hachage est vérifié.

"151920920 de" Hachage:

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

vérifiant:

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);
}
185
répondu Andrew Savinykh 2017-03-03 13:38:08

parce que ces jours - ci ASP.NET est open source, vous pouvez le trouver sur GitHub: AspNet.Identité 3.0 et AspNet.Identité 2.0 .

des commentaires:

/* =======================
 * 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.)
 */
35
répondu Knelis 2018-06-07 07:57:39

je comprends la réponse acceptée, et l'ai voté mais j'ai pensé que je laisserais la réponse de mes laïcs ici...

création d'un hachage

  1. le sel est généré au hasard en utilisant la fonction Rfc2898DeriveBytes qui génère un hash et un sel. Entrées à Rfc2898DeriveBytes est le mot de passe, la taille du sel à générer et le nombre d'itérations de hachage effectuer. https://msdn.microsoft.com/en-us/library/h83s4e12 (v=V110).aspx
  2. le sel et le haschisch sont ensuite broyés ensemble(sel d'abord suivi par le hachage) et encodé sous forme de chaîne (de sorte que le sel est encodé dans le hachage.) Cette empreinte numérique (qui contient le sel et le hachage) est alors stocké (typiquement) dans la base de données contre l'utilisateur.

vérification d'un mot de passe contre un hachage

pour vérifier un mot de passe qu'un utilisateur entre.

  1. le sel est extrait du mot de passe caché.
  2. le sel est utilisé pour hacher le mot de passe d'entrée des utilisateurs en utilisant une surcharge de Rfc2898DeriveBytes qui prend un sel au lieu d'en générer un. https://msdn.microsoft.com/en-us/library/yx129kfs (v=V110).aspx
  3. Le hash stocké et le le hachage d'essai est ensuite comparé.

Hachage

sous les couvertures le hachage est généré en utilisant la fonction de hachage SHA1 ( https://en.wikipedia.org/wiki/SHA-1 ). Cette fonction est itérativement appelée 1000 fois (dans l'implémentation de L'identité par défaut)

pourquoi cette sécurité

  • "sels Aléatoires" s'entend: l'attaquant ne peut pas utiliser une table pré-générée des hashs pour briser les mots de passe. Ils ont besoin de générer un table de hachage pour chaque sel. (En supposant ici que le hacker a aussi compromis votre sel)
  • si 2 mots de passe sont identiques ils seront ont différentes valeurs de hachage. (ce qui signifie que les attaquants ne peuvent pas inférer " commun’ mots de passe)
  • appel itératif SHA1 1000 fois signifie que le attaquant doit également faire. L'idée étant qu'à moins d'avoir le temps sur un superordinateur ils n'auront pas assez de ressources pour jouer forcez le mot de passe du hachage. Il serait massivement ralentir le temps pour générer une table de hachage pour un sel.
20
répondu Nattrass 2017-09-06 08:36:22

pour ceux comme moi qui sont tout nouveau à cela, Voici code avec const et une réelle façon de comparer les octets []. J'ai obtenu tout ce code de stackoverflow mais j'ai défini consts pour que les valeurs puissent être changées et aussi

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

dans votre application customusermanager, vous définissez la propriété PasswordHasher le nom de la classe qui contient le code ci-dessus.

7
répondu kfrosty 2016-02-12 20:51:43