Je me demande si le hasher de mots de passe implémenté par défaut dans le serManager fourni avec MVC 5 et ASP.NET Identity Framework est suffisamment sécurisé? Et si oui, si vous pouviez m'expliquer comment cela fonctionne?
L'interface IPasswordHasher ressemble à ceci:
public interface IPasswordHasher
{
string HashPassword(string password);
PasswordVerificationResult VerifyHashedPassword(string hashedPassword,
string providedPassword);
}
Comme vous pouvez le constater, cela ne prend pas un sel, mais il est mentionné dans ce fil de discussion: " hachage du mot de passe d'identité Asp.net " qu'il infact le sel dans les coulisses. Alors je me demande comment il fait ça? Et d'où vient ce sel?
Ce qui me préoccupe, c'est que le sel est statique, ce qui le rend assez instable.
Voici comment implémentation par défaut . Il utilise un fonction de dérivation de clé avec un sel aléatoire pour produire le hachage. Le sel est inclus dans la sortie de la KDF. Ainsi, chaque fois que vous "hachez" le même mot de passe, vous obtiendrez des hachages différents. Pour vérifier le hachage, la sortie est divisée en sel et le reste et le fichier KDF est exécuté à nouveau avec 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é.
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érification:
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);
}
Comme ces jours ASP.NET est open source, vous pouvez le trouver sur GitHub: AspNet.Identity 3. et AspNet.Identity 2. .
D'après les 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.)
*/
Je comprends la réponse acceptée et je l’ai votée favorablement, mais j’ai pensé jeter la réponse de mon profane ici ...
Création d'un hachage
Vérification d'un mot de passe par rapport à un dièse
Pour vérifier un mot de passe saisi par un utilisateur.
Le hachage
Sous les couvertures, le hachage est généré à l'aide de la fonction de hachage SHA1 ( https://en.wikipedia.org/wiki/SHA-1 ). Cette fonction est appelée de manière itérative 1000 fois (dans l'implémentation d'Identity par défaut)
Pourquoi est-ce sécurisé
Pour ceux qui comme moi sont nouveaux dans ce domaine, voici le code avec const et un moyen réel de comparer les octets []. Je suis tout ce code de stackoverflow mais consts définis afin 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 ApplicationUserManager personnalisé, vous définissez la propriété PasswordHasher avec le nom de la classe contenant le code ci-dessus.