web-dev-qa-db-fra.com

Hachage des mots de passe avec MD5 ou sha-256 C #

J'écris un formulaire d'inscription pour une application mais j'ai toujours des problèmes pour être nouveau sur c #.

Je cherche à chiffrer/hacher les mots de passe en md5 ou sha-256, de préférence sha-256.

De bons exemples? Je veux qu'il puisse prendre les informations du "mot de passe de chaîne"; puis le hacher et le stocker dans la variable "string hPassword;". Des idées?

43
Sean

N'utilisez pas de hachage simple, ni même de hachage salé. Utilisez une sorte de technique de renforcement des clés comme bcrypt (avec un implémentation .NET ici ) ou PBKDF2 (avec un built- en cours de mise en œuvre ).

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 puis pour tester si un 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!");
}
78
LukeH

Vous allez vouloir utiliser le System.Security.Cryptography espace de noms; en particulier, le MD5 class ou SHA256 classe .

En tirant un peu du code sur cette page , et sachant que les deux classes ont la même classe de base ( HashAlgorithm ), vous pouvez utiliser une fonction comme ça:

public string ComputeHash(string input, HashAlgorithm algorithm)
{
   Byte[] inputBytes = Encoding.UTF8.GetBytes(input);

   Byte[] hashedBytes = algorithm.ComputeHash(inputBytes);

   return BitConverter.ToString(hashedBytes);
}

Ensuite, vous pourriez l'appeler comme ça (pour MD5):

string hPassword = ComputeHash(password, new MD5CryptoServiceProvider());

Ou pour SHA256:

string hPassword = ComputeHash(password, new SHA256CryptoServiceProvider());

Edit: Ajout du support Salt
Comme le souligne dtb dans les commentaires, ce code serait plus fort s'il incluait la possibilité d'ajouter sel . Si vous ne le connaissez pas, salt est un ensemble de bits aléatoires qui sont inclus en tant qu'entrée dans la fonction de hachage, ce qui permet de déjouer les attaques de dictionnaire contre un mot de passe haché (par exemple, en utilisant un Rainbow table ). Voici une version modifiée de la fonction ComputeHash qui prend en charge le sel:

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

J'espère que cela vous a été utile!

53
Donut

Vous devez toujours saler le mot de passe avant de le hacher lors de son stockage dans la base de données.

Colonnes de base de données recommandées:

  • PasswordSalt: int
  • PasswordHash: binaire (20)

La plupart des publications que vous trouverez en ligne parleront de ASCII encodant le sel et le hachage, mais ce n'est pas nécessaire et n'ajoutez que 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. Je comprends votre question sur SHA-256, mais à moins que vous n'ayez une raison impérieuse, utiliser SHA-1 avec une valeur de sel être suffisant dans la plupart des pratiques commerciales. Si vous insistez sur SHA-256, le champ de hachage dans la base de données doit avoir une longueur de 32 octets.

Vous trouverez ci-dessous quelques fonctions qui généreront le sel, calculeront le hachage et vérifieront le hachage par rapport à un mot de passe.

La fonction salt ci-dessous génère un sel cryptographiquement fort sous forme d'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 ensuite être haché en utilisant le sel avec la fonction ci-dessous. Le sel est concaténé au 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);
}

La vérification du mot de passe peut être effectuée simplement en calculant le hachage puis en le comparant au hachage attendu.


private bool IsPasswordValid(string passwordToValidate, int salt, byte[] correctPasswordHash)
{
    byte[] hashedPassword = ComputePasswordHash(passwordToValidate, salt);

    return hashedPassword.SequenceEqual(correctPasswordHash);
}

5
Adam Spicer

Voici une implémentation complète d'une classe SecuredPassword ignorant la persistance

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 tests:

 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);
    }
}
2
Sam Shiles

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 facilite une attaque par force brute sur les mots de passe si quelqu'un accède à votre base de données.

Lisez cet article: Assez avec les tables arc-en-ciel: ce que vous devez savoir sur les schémas de mots de passe sécurisés et ceci réponse à une précédente SO question .

Quelques citations de l'article:

Le problème est que MD5 est rapide. Ses concurrents modernes, comme SHA1 et SHA256. La vitesse est un objectif de conception d'un hachage sécurisé moderne, car les hachages sont un élément constitutif de presque tous les systèmes de cryptage et sont généralement exécutés à la demande par paquet ou par message.

La vitesse est exactement ce que vous ne voulez pas dans une fonction de hachage de mot de passe.


Enfin, 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.

2
Jeff Ogata

TL; DR utilise Microsoft.AspNetCore.Cryptography.KeyDerivation , implémentant PBKDF2 avec SHA-512.

La bonne idée pour commencer avec le hachage de mot de passe est de regarder ce que directives OWASP disent. La liste des algorithmes recommandés comprend Argon2, PBKDF2, scrypt et bcrypt. Tous ces algorithmes peuvent être réglés pour ajuster le temps nécessaire au hachage d'un mot de passe et, en conséquence, le temps de le casser via la force brute. Tous ces algorithmes utilisent du sel pour se protéger des attaques des tables Rainbow.

Aucun de ces algorithmes n'est terriblement faible, mais il existe quelques différences:

  • bcrypt existe depuis près de 20 ans, a été largement utilisé et a résisté à l'épreuve du temps. Il est assez résistant aux attaques GPU, mais pas aux FPGA
  • Argon2 est le plus récent ajout, étant un gagnant du concours de hachage de mot de passe 2015. 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 par GPU et FPGA, mais j'ai entendu dire qu'il ne s'est pas avéré aussi puissant que ce qui avait été annoncé à l'origine.
  • PBKDF2 est une famille d'algorithmes paramétrés par les différentes fonctions de hachage. Il n'offre pas de protection spécifique contre le GPU ou les attaques ASIC, en particulier si une fonction de hachage plus faible comme SHA-1 est utilisée, mais il est cependant certifié FIPS si cela vous importe, et toujours acceptable si le nombre d'itérations est suffisamment grand.

Basé uniquement sur des algorithmes, j'irais probablement avec bcrypt, PBKDF2 étant le moins favorable.

Cependant, ce n'est pas l'histoire complète, car même le meilleur algorithme peut être rendu non sécurisé par une mauvaise implémentation. Voyons 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. Actuellement, il y a 6 contributeurs et 8 problèmes (tous fermés) sur github. Dans l'ensemble, cela 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 que Stack Overflow s'est éloigné de l'utilisation de bcrypt pour de telles raisons
  • La meilleure façon d'utiliser Argon2 est probablement de se lier à la bibliothèque libsodium bien connue, par ex. https://github.com/adamcaudill/libsodium-net . L'idée est que la majeure partie de la cryptographie est implémentée via libsodium, qui a un support considérable, et les parties `` non testées '' sont assez limitées. Cependant, dans les détails de la cryptographie, cela signifie beaucoup, donc combiné avec Argon2 étant relativement récent, je le traiterais comme une option expérimentale
  • Pendant longtemps, .NET a intégré une implémentation de PBKDF2 via la classe Rfc2898DeriveBytes . 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.Cryptography.KeyDerivation package disponible via NuGet. Il fournit un algorithme PBKDF2 avec des fonctions de hachage SHA-1, SHA-256 ou SHA-512, 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 évaluer correctement la diligence cryptographique des développeurs Microsoft par rapport aux développeurs BCrypt.net ou libsodium, il est logique de lui faire confiance car si vous exécutez une application .NET, vous comptent déjà beaucoup sur Microsoft. Nous pouvons également nous attendre à ce que Microsoft publie des mises à jour si des problèmes de sécurité sont détectés. J'espère.

Pour résumer la recherche jusqu'à ce point, alors que PBKDF2 pourrait être l'algorithme le moins préféré des quatre, la disponibilité de l'implémentation fournie par Microsoft l'emporte sur, donc la décision raisonnable serait d'utiliser Microsoft.AspNetCore.Cryptography.KeyDerivation.

Le package récent pour le moment cible .NET Standard 2.0, donc disponible dans .NET Core 2.0 ou .NET Framework 4.6.1 ou version ultérieure. Si vous utilisez une version antérieure du framework, il est possible d'utiliser la version précédente du package, 1.1. , qui cible .NET Framework 4.5.1 ou .NET Core 1.0. Malheureusement, il n'est pas possible de l'utiliser dans des versions encore plus anciennes de .NET.

La documentation et l'exemple de travail sont disponibles sur docs.Microsoft.com . Cependant, ne copiez-collez pas tel quel, il y a toujours des décisions qu'un développeur doit prendre.

La première décision est de savoir quelle fonction de hachage utiliser. Les options disponibles incluent SHA-1, SHA-256 et SHA-512. Parmi ceux-ci, SHA-1 est définitivement trop rapide pour être sécurisé, SHA-256 est décent, mais je recommanderais SHA-512, car soi-disant, son utilisation des opérations 64 bits rend plus difficile de bénéficier d'attaques basées sur GPU.

Ensuite, vous devez choisir la longueur de sortie du hachage du mot de passe et la longueur du sel. Cela n'a pas de sens 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 le plus sûr de l'avoir exactement comme ça. Pour la longueur du sel, les avis divergent. 128 bits devraient suffire, mais dans tous les cas, la longueur plus longue que la longueur de sortie de hachage n'offre certainement aucun avantage.

Ensuite, il y a un nombre d'itérations. Plus il est grand, plus les hachages de mots de passe sont difficiles à déchiffrer, mais plus il faut de temps pour connecter les utilisateurs. ne doit pas être inférieur à 10000.

Normalement, vous obtiendrez un tableau d'octets sous forme de 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 sel et mot de passe dans une seule colonne à l'aide d'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 permette de passer en toute transparence à un meilleur algorithme de hachage à l'avenir.

2
ovolko

PBKDF2 utilise HMACSHA1 ....... si vous voulez une implémentation plus moderne de HMACSHA256 ou HMACSHA512 et que vous voulez toujours étirer les clés pour rendre l'algorithme plus lent Je suggère cette API: https://sourceforge.net/projects/pwdtknet /

2
thashiznets

Veuillez utiliser ceci car j'ai les mêmes problèmes avant mais pourrais le résoudre sera l'extrait de code litle

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

    }
1
king zecole

La classe System.Security.Cryptography.SHA256 devrait faire l'affaire:

http://msdn.Microsoft.com/en-us/library/system.security.cryptography.sha256.aspx

1
James Kovacs