Hachage des mots de passe avec MD5 ou sha-256 C#
j'écris un formulaire d'inscription pour une demande, mais j'ai toujours des problèmes avec le fait d'être nouveau au c#.
je cherche à chiffrer/hacher les mots de passe à md5 ou sha-256, de préférence sha-256.
de bons exemples? Je veux qu'il soit capable de prendre les informations de "string password;" et puis le hachage et stocker dans la variable "string hPassword;". Des idées?
9 réponses
ne pas utiliser un simple hash, ou même un hash salé. Utilisez une sorte de technique de renforcement des clés comme bcrypt (avec une implémentation .NET ici ) ou PBKDF2 (avec une implémentation intégrée ).
voici un exemple d'utilisation de PBKDF2.
pour générer une clé à partir de votre mot de passe...
string password = GetPasswordFromUserInput();
// specify that we want to randomly generate a 20-byte salt
using (var deriveBytes = new Rfc2898DeriveBytes(password, 20))
{
byte[] salt = deriveBytes.Salt;
byte[] key = deriveBytes.GetBytes(20); // derive a 20-byte key
// save salt and key to database
}
et ensuite de tester si un le mot de passe est valide...
string password = GetPasswordFromUserInput();
byte[] salt, key;
// load salt and key from database
using (var deriveBytes = new Rfc2898DeriveBytes(password, salt))
{
byte[] newKey = deriveBytes.GetBytes(20); // derive a 20-byte key
if (!newKey.SequenceEqual(key))
throw new InvalidOperationException("Password is invalid!");
}
vous allez vouloir utiliser l'espace de nom System.Security.Cryptography
; plus précisément, le MD5
classe ou le SHA256
classe .
tirer un peu du code sur cette page , et en sachant que les deux classes ont la même classe de base( HashAlgorithm
), vous pouvez utiliser une fonction comme celle-ci:
public string ComputeHash(string input, HashAlgorithm algorithm)
{
Byte[] inputBytes = Encoding.UTF8.GetBytes(input);
Byte[] hashedBytes = algorithm.ComputeHash(inputBytes);
return BitConverter.ToString(hashedBytes);
}
alors vous pouvez l'appeler comme ceci (pour MD5):
string hPassword = ComputeHash(password, new MD5CryptoServiceProvider());
ou pour SHA256:
string hPassword = ComputeHash(password, new SHA256CryptoServiceProvider());
Modifier: Ajouter Le Soutien De Sel
Comme le dtb l'a souligné dans les commentaires, ce code serait plus fort s'il incluait la possibilité d'ajouter sel . Si vous n'êtes pas familier avec elle, salt est un ensemble de bits aléatoires qui sont inclus comme une entrée à fonction de hachage, qui va un long chemin pour contrecarrer les attaques de dictionnaire contre un mot de passe hachée (par exemple, en utilisant un table arc-en-ciel ). Voici une version modifiée de la fonction ComputeHash
qui supporte salt:
public static string ComputeHash(string input, HashAlgorithm algorithm, Byte[] salt)
{
Byte[] inputBytes = Encoding.UTF8.GetBytes(input);
// Combine salt and input bytes
Byte[] saltedInput = new Byte[salt.Length + inputBytes.Length];
salt.CopyTo(saltedInput, 0);
inputBytes.CopyTo(saltedInput, salt.Length);
Byte[] hashedBytes = algorithm.ComputeHash(saltedInput);
return BitConverter.ToString(hashedBytes);
}
Espère que cela vous a été utile!
vous devez toujours saler le mot de passe avant de les Hasher lors de leur stockage dans la base de données.
Recommandé colonnes de base de données:
- PasswordSalt: int
- PasswordHash: binaire (20)
la plupart des billets que vous trouverez en ligne parleront D'ASCII encodant le sel et le hash, mais cela n'est pas nécessaire et ne fait qu'ajouter des calculs inutiles. Aussi si vous utilisez SHA-1 , alors la sortie ne sera que de 20 octets donc votre champ de hachage dans la base de données n'a besoin que de 20 octets de longueur. Je comprends vos questions au sujet de SHA-256, mais à moins que vous n'ayez une raison convaincante, l'utilisation de SHA-1 avec une valeur de sel sera suffisante dans la plupart des pratiques commerciales. Si vous insistez sur SHA-256, alors le champ de hachage dans la base de données doit avoir 32 octets de longueur.
ci-dessous sont quelques fonctions qui généreront le sel, calculeront le hachage et vérifier le hachage contre un mot de passe.
la fonction salt ci-dessous génère un sel cryptographiquement fort comme un entier à partir de 4 octets aléatoires créés cryptographiquement.
private int GenerateSaltForPassword()
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] saltBytes = new byte[4];
rng.GetNonZeroBytes(saltBytes);
return (((int)saltBytes[0]) << 24) + (((int)saltBytes[1]) << 16) + (((int)saltBytes[2]) << 8) + ((int)saltBytes[3]);
}
le mot de passe peut alors être hachée en utilisant le sel avec la fonction ci-dessous. Le sel est concaténé avec le mot de passe, puis le hachage est calculé.
private byte[] ComputePasswordHash(string password, int salt)
{
byte[] saltBytes = new byte[4];
saltBytes[0] = (byte)(salt >> 24);
saltBytes[1] = (byte)(salt >> 16);
saltBytes[2] = (byte)(salt >> 8);
saltBytes[3] = (byte)(salt);
byte[] passwordBytes = UTF8Encoding.UTF8.GetBytes(password);
byte[] preHashed = new byte[saltBytes.Length + passwordBytes.Length];
System.Buffer.BlockCopy(passwordBytes, 0, preHashed, 0, passwordBytes.Length);
System.Buffer.BlockCopy(saltBytes, 0, preHashed, passwordBytes.Length, saltBytes.Length);
SHA1 sha1 = SHA1.Create();
return sha1.ComputeHash(preHashed);
}
Vérifier le mot de passe peut être fait simplement en calculant le hachage et puis je le compare au hash attendu.
private bool IsPasswordValid(string passwordToValidate, int salt, byte[] correctPasswordHash)
{
byte[] hashedPassword = ComputePasswordHash(passwordToValidate, salt);
return hashedPassword.SequenceEqual(correctPasswordHash);
}
si vous allez stocker les mots de passe hachés, utilisez bcrypt au lieu de SHA-256. Le problème est que SHA-256 est optimisé pour la vitesse, ce qui rend plus facile pour une attaque de force brute sur les mots de passe si quelqu'un obtient l'accès à votre base de données.
Lire cet article: Assez Avec Les Tables arc-en-ciel: Ce que Vous Devez Savoir à Propos de Mot de passe Sécurisé Régimes et de cette réponse à une SORTE question.
quelques citations de l'article:
le problème est que MD5 est rapide. Tout comme ses concurrents modernes, comme SHA1 et SHA256. La vitesse est un objectif de conception d'un hachage sûr moderne, parce que les hachages sont un bloc de construction de presque chaque cryptosystem, et obtiennent généralement la demande-exécutée sur une base par paquet ou par message.
la vitesse est exactement ce que vous ne voulez pas dans une fonction de hachage de mot de passe.
finalement, nous avons appris que si nous voulons stocker les mots de passe en toute sécurité, nous avons trois options raisonnables: le schéma MD5 de PHK, le schéma Bcrypt de Provos-Maziere et SRP. Nous avons appris que le bon choix est Bcrypt.
PBKDF2 utilise HMACSHA1.......si vous voulez une implémentation plus moderne de HMACSHA256 ou HMACSHA512 et que vous voulez tout de même que key stretching ralentisse l'algorithme, je suggère cette API: https://sourceforge.net/projects/pwdtknet/
Voici une mise en œuvre complète d'une persistence unknown SecuredPassword class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
public class SecuredPassword
{
private const int saltSize = 256;
private readonly byte[] hash;
private readonly byte[] salt;
public byte[] Hash
{
get { return hash; }
}
public byte[] Salt
{
get { return salt; }
}
public SecuredPassword(string plainPassword)
{
if (string.IsNullOrWhiteSpace(plainPassword))
return;
using (var deriveBytes = new Rfc2898DeriveBytes(plainPassword, saltSize))
{
salt = deriveBytes.Salt;
hash = deriveBytes.GetBytes(saltSize);
}
}
public SecuredPassword(byte[] hash, byte[] salt)
{
this.hash = hash;
this.salt = salt;
}
public bool Verify(string password)
{
if (string.IsNullOrWhiteSpace(password))
return false;
using (var deriveBytes = new Rfc2898DeriveBytes(password, salt))
{
byte[] newKey = deriveBytes.GetBytes(saltSize);
return newKey.SequenceEqual(hash);
}
}
}
et essais:
public class SecuredPasswordTests
{
[Test]
public void IsHashed_AsExpected()
{
var securedPassword = new SecuredPassword("password");
Assert.That(securedPassword.Hash, Is.Not.EqualTo("password"));
Assert.That(securedPassword.Hash.Length, Is.EqualTo(256));
}
[Test]
public void Generates_Unique_Salt()
{
var securedPassword = new SecuredPassword("password");
var securedPassword2 = new SecuredPassword("password");
Assert.That(securedPassword.Salt, Is.Not.Null);
Assert.That(securedPassword2.Salt, Is.Not.Null);
Assert.That(securedPassword.Salt, Is.Not.EqualTo(securedPassword2.Salt));
}
[Test]
public void Generates_Unique_Hash()
{
var securedPassword = new SecuredPassword("password");
var securedPassword2 = new SecuredPassword("password");
Assert.That(securedPassword.Hash, Is.Not.Null);
Assert.That(securedPassword2.Hash, Is.Not.Null);
Assert.That(securedPassword.Hash, Is.Not.EqualTo(securedPassword2.Hash));
}
[Test]
public void Verify_WhenMatching_ReturnsTrue()
{
var securedPassword = new SecuredPassword("password");
var result = securedPassword.Verify("password");
Assert.That(result, Is.True);
}
[Test]
public void Verify_WhenDifferent_ReturnsFalse()
{
var securedPassword = new SecuredPassword("password");
var result = securedPassword.Verify("Password");
Assert.That(result, Is.False);
}
[Test]
public void Verify_WhenRehydrated_AndMatching_ReturnsTrue()
{
var securedPassword = new SecuredPassword("password123");
var rehydrated = new SecuredPassword(securedPassword.Hash, securedPassword.Salt);
var result = rehydrated.Verify("password123");
Assert.That(result, Is.True);
}
[Test]
public void Constructor_Handles_Null_Password()
{
Assert.DoesNotThrow(() => new SecuredPassword(null));
}
[Test]
public void Constructor_Handles_Empty_Password()
{
Assert.DoesNotThrow(() => new SecuredPassword(string.Empty));
}
[Test]
public void Verify_Handles_Null_Password()
{
Assert.DoesNotThrow(() => new SecuredPassword("password").Verify(null));
}
[Test]
public void Verify_Handles_Empty_Password()
{
Assert.DoesNotThrow(() => new SecuredPassword("password").Verify(string.Empty));
}
[Test]
public void Verify_When_Null_Password_ReturnsFalse()
{
Assert.That(new SecuredPassword("password").Verify(null), Is.False);
}
}
Le Système.Sécurité.Cryptographie.La classe SHA256 devrait faire l'affaire:
http://msdn.microsoft.com/en-us/library/system.security.cryptography.sha256.aspx
s'il vous Plaît utiliser ce que j'ai le même problèmes avant, mais pourrait résoudre ce sera la petit extrait de code
public static string ComputeHash(string input, HashAlgorithm algorithm, Byte[] salt)
{
Byte[] inputBytes = Encoding.UTF8.GetBytes(input);
// Combine salt and input bytes
Byte[] saltedInput = new Byte[salt.Length + inputBytes.Length];
salt.CopyTo(saltedInput, 0);
inputBytes.CopyTo(saltedInput, salt.Length);
Byte[] hashedBytes = algorithm.ComputeHash(saltedInput);
StringBuilder hex = new StringBuilder(hashedBytes.Length * 2);
foreach (byte b in hashedBytes)
hex.AppendFormat("{0:X2}", b);
return hex.ToString();
}
TL;DR utiliser Microsoft.AspNetCore.Cryptographie.KeyDerivation , implementing PBKDF2 with SHA-512.
la bonne idée pour commencer avec le hachage de mot de passe est de regarder ce que lignes directrices OWASP disent. La liste des algorithmes recommandés comprend Argon2, PBKDF2, scrypt et bcrypt. Tous ces algorithmes peuvent être réglés pour adapter le temps qu'il faut pour hacher un mot de passe, et, en conséquence, le temps de la fissure par brute-force. Tous ces algorithmes utilisent le sel pour protéger des tables arc-en-attaques.
aucun de ces algorithmes N'est terriblement faible, mais il y a quelques différences:
- bcrypt a été autour de près de 20 ans, a été largement utilisé et a résisté à l'épreuve du temps. Il est assez résistant au GPU attaques, mais pas à FPGA
- Argon2 est le plus récent ajout, être un gagnant de 2015 mot de passe hashing concurrence. Il a une meilleure protection contre les attaques GPU et FPGA, mais est un peu trop récent à mon goût
- Je ne sais pas grand chose sur scrypt. Il a été conçu pour contrecarrer les attaques accélérées de la GPU - et de la FPGA -, mais j'ai entendu dire qu'il s'est avéré qu'il n'était pas aussi fort qu'on le prétendait à l'origine
- PBKDF2 est une famille d'algorithmes paramétrés par les différents fonction. Il n'offre pas de protection spécifique contre les attaques GPU ou ASIC, en particulier si un une fonction de hachage plus faible comme SHA - 1 est utilisée, mais elle est certifiée FIPS si cela vous importe, et elle est encore acceptable si le nombre d'itérations est assez important.
basé sur des algorithmes seulement, je dirais probablement que bcrypt, PBKDF2 étant le moins favorable.
cependant, ce n'est pas l'histoire complète, parce que même le meilleur algorithme peut être rendu incertain par une mauvaise implémentation. Regardons ce qui est disponible pour la plate-forme. net:
- Bcrypt est disponible via bcrypt.net . Ils disent que L'implémentation est basée sur Java jBCrypt. Il y a actuellement 6 contributeurs et 8 numéros (tous fermés) sur github. Dans l'ensemble, il semble bon, cependant, je ne sais pas si quelqu'un a fait un audit du code, et il est difficile de dire si une version mise à jour sera disponible assez tôt si une vulnérabilité est trouvée. J'ai entendu dire que le débordement de la pile s'est éloigné de l'utilisation de bcrypt à cause d'une telle motifs 1519140920"
- probablement la meilleure façon d'utiliser L'Argon2 est à travers les fixations bibliothèque libsodium bien connue, p.ex. https://github.com/adamcaudill/libsodium-net . L'idée est que la plus grande partie de la crypto est mise en œuvre via libsodium, qui a considérable le soutien, et les parties "non testées" sont assez limités. Toutefois, dans les détails de cryptographie signifient beaucoup, donc combiné avec Argon2 étant relativement récent, je le considérerais comme une option expérimentale.
- pendant longtemps, .NET a intégré une implémentation de PBKDF2 via Rfc2898DeriveBytes de la classe. Cependant, l'implémentation ne peut utiliser que la fonction de hachage SHA-1, qui est jugée trop rapide pour être sécurisée de nos jours
- enfin, la solution la plus récente est
Microsoft.AspNetCore.Cryptographie.Bloc-notes
disponible via NuGet. Il fournit l'algorithme PBKDF2 avec SHA-1, SHA-256, ou SHA-512 hash fonctions, ce qui est considérablement mieux que
Rfc2898DeriveBytes
. Le plus grand avantage ici est que L'implémentation est fournie par Microsoft, et bien que je ne puisse pas correctement évaluer la diligence cryptographique des développeurs de Microsoft versus BCrypt.net ou les développeurs libsodium, il est tout simplement logique de lui faire confiance parce que si vous exécutez une application .NET, vous êtes fortement compter sur Microsoft déjà. Nous pourrions également nous attendre à ce que Microsoft publie des mises à jour si des problèmes de sécurité sont découverts. Avec de la chance.
pour résumer la recherche jusqu'à présent, alors que PBKDF2 pourrait être l'algorithme le moins préféré des Quatre, la disponibilité de Microsoft-fourni mise en œuvre trumps que, donc la décision raisonnable serait d'utiliser Microsoft.AspNetCore.Cryptography.KeyDerivation
.
le paquet récent à l'heure actuelle cible .NET standard 2.0, donc disponible dans .NET Core 2.0 ou .net Framework 4.6.1 ou plus tard. Si vous utilisez la version précédente du cadre, il est possible d'utiliser la version précédente du paquet, 1.1.3 , qui cible. Malheureusement, il n'est pas possible de l'utiliser dans des versions antérieures .NET.
la documentation et l'exemple de travail sont disponibles à docs.microsoft.com . Cependant, ne copiez pas-collez-le comme il est, il ya encore des décisions qu'un développeur doit prendre.
la première décision est ce que la fonction de hachage à utiliser. Options disponibles inclure SHA-1, SHA-256 et SHA-512. Parmi ceux-ci, SHA-1 est certainement trop rapide pour être sécurisé, SHA-256 est décent, mais je recommande SHA-512, parce que soi-disant, son utilisation 64 bits des opérations rend plus difficile de bénéficier des attaques basées sur GPU.
ensuite, vous devez choisir la longueur de sortie de hachage de mot de passe et la longueur de sel. Il n'est pas logique d'avoir une sortie plus longue que la sortie de la fonction de hachage (par exemple 512 bits pour SHA-512), et ce serait probablement la plus sûre de l'avoir exactement comme cela. Pour la longueur de sel, les opinions diffèrent. 128 bits devraient suffire, mais dans tous les cas, la longueur plus longue que la longueur de sortie de hachage ne fournit certainement aucun avantage.
Ensuite, il y a un nombre d'itérations. Plus elle est grande, plus les hashs sont à craquer, mais en plus il faut connecter les utilisateurs. Je suggère de le choisir de façon à ce que le hachage prenne 0,25 - 1 seconde sur le système de production typique, et dans tous les cas, il ne devrait pas être inférieur à 10000.
normalement, vous obtiendriez un tableau d'octets comme valeurs de sel et de hachage. Utilisez Base64 pour les convertir en chaînes. Vous pouvez choisir d'utiliser deux colonnes différentes dans la base de données, ou combiner le sel et le mot de passe dans une colonne en utilisant un séparateur qui n'est pas rencontré dans Base64.
n'oubliez pas de concevoir un stockage de hachage de mot de passe d'une manière qui permet de passer en toute transparence à un meilleur algorithme de hachage à l'avenir.