it-swarm.com.de

Hashing von Passwörtern mit MD5 oder sha-256 C #

Ich schreibe ein Anmeldeformular für eine Bewerbung, habe aber immer noch Probleme, neu bei c # zu sein.

Ich suche nach Verschlüsselung/Hash-Passwörtern für md5 oder sha-256, vorzugsweise sha-256.

Irgendwelche guten Beispiele? Ich möchte, dass es in der Lage ist, die Informationen aus "string password" zu übernehmen. und hash es dann und speichere es in der Variablen "string hPassword;". Irgendwelche Ideen?

43
Sean

Verwenden Sie keinen einfachen Hasch oder gar einen gesalzenen Hasch. Verwenden Sie eine Art Schlüsselverstärkungstechnik wie bcrypt (mit einer . NET-Implementierung hier ) oder PBKDF2 (mit einer eingebauten) in Umsetzung ).

Hier ist ein Beispiel mit PBKDF2.

So generieren Sie einen Schlüssel aus Ihrem Passwort ...

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
}

Und dann testen, ob ein Passwort gültig ist ...

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

Du wirst das System.Security.Cryptography Namespace; speziell die MD5 Klasse oder das SHA256 Klasse .

Zeichnen Sie ein bisschen aus dem Code auf diese Seite und mit dem Wissen, dass beide Klassen dieselbe Basisklasse haben ( HashAlgorithm ), könnten Sie eine Funktion verwenden so was:

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

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

   return BitConverter.ToString(hashedBytes);
}

Dann könnte man es so nennen (für MD5):

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

Oder für SHA256:

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

Bearbeiten: Hinzufügen von Salt Support
Wie dtb in den Kommentaren hervorhob, wäre dieser Code stärker, wenn er die Fähigkeit enthalten würde, Salz hinzuzufügen. Wenn Sie nicht damit vertraut sind, besteht Salt aus einer Reihe von Zufallsbits, die als Eingabe für die Hashing - Funktion verwendet werden. Dies verhindert erheblich Wörterbuchangriffe gegen ein gehashtes Kennwort (z. B. mit einem Rainbow Tabelle ). Hier ist eine modifizierte Version der ComputeHash -Funktion, die salt unterstützt:

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

Hoffe das war hilfreich!

53
Donut

Sie sollten das Passwort immer vor dem Hashing salzen, wenn Sie es in der Datenbank speichern.

Empfohlene Datenbankspalten:

  • PasswordSalt: int
  • PasswordHash: binär (20)

Die meisten Beiträge, die Sie online finden, sprechen von ASCII Codierung von Salt und Hash, aber das ist nicht erforderlich und fügt nur nicht benötigte Berechnungen hinzu. Auch wenn Sie SHA-1 verwenden, Dann beträgt die Ausgabe nur 20 Byte, sodass Ihr Hash-Feld in der Datenbank nur 20 Byte lang sein muss. Ich verstehe, dass Sie nach SHA-256 gefragt haben, aber wenn Sie keinen zwingenden Grund haben, wird SHA-1 mit einem Salt-Wert verwendet Wenn Sie auf SHA-256 bestehen, muss das Hash-Feld in der Datenbank 32 Byte lang sein.

Im Folgenden sind einige Funktionen aufgeführt, mit denen das Salt generiert, der Hash berechnet und der Hash anhand eines Kennworts überprüft wird.

Die Salt-Funktion unten generiert ein kryptografisch starkes Salt als Ganzzahl aus 4 kryptografisch erstellten Zufallsbytes.

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

Das Passwort kann dann mit der folgenden Funktion mit dem Salt gehasht werden. Das Salt wird mit dem Passwort verknüpft und dann der Hash berechnet.


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

Das Passwort kann einfach überprüft werden, indem der Hash berechnet und dann mit dem erwarteten Hash verglichen wird.


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

    return hashedPassword.SequenceEqual(correctPasswordHash);
}

5
Adam Spicer

Hier ist eine vollständige Implementierung einer Persistenzklasse, die SecuredPassword nicht kennt

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

Und 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

Wenn Sie die Hash-Passwörter speichern möchten, verwenden Sie bcrypt anstelle von SHA-256. Das Problem ist, dass SHA-256 auf Geschwindigkeit optimiert ist, was einen Brute-Force-Angriff auf Passwörter erleichtert, sollte jemand Zugriff auf Ihre Datenbank erhalten.

Lesen Sie diesen Artikel: Genug mit den Rainbow-Tabellen: Was Sie über sichere Passwortschemata wissen müssen und dies Antwort auf eine vorherige SO) Frage .

Einige Zitate aus dem Artikel:

Das Problem ist, dass MD5 schnell ist. So sind seine modernen Konkurrenten, wie SHA1 und SHA256. Geschwindigkeit ist ein Entwurfsziel eines modernen sicheren Hashes, da Hashes ein Baustein für fast jedes Kryptosystem sind und in der Regel paket- oder nachrichtenbezogen ausgeführt werden.

Geschwindigkeit ist genau das, was Sie in einer Passwort-Hash-Funktion nicht wollen.


Schließlich haben wir erfahren, dass wir drei sinnvolle Möglichkeiten haben, um Passwörter sicher zu speichern: das MD5-Schema von PHK, das Bcrypt-Schema von Provos-Maziere und SRP. Wir haben erfahren, dass Bcrypt die richtige Wahl ist.

2
Jeff Ogata

TL; DR use Microsoft.AspNetCore.Cryptography.KeyDerivation , Implementierung von PBKDF2 mit SHA-512.

Die gute Idee, um mit dem Passwort-Hashing zu beginnen, ist es, sich anzuschauen, was OWASP-Richtlinien sagt. Die Liste der empfohlenen Algorithmen umfasst Argon2, PBKDF2, Scrypt und Bcrypt. Alle diese Algorithmen können angepasst werden, um die Zeit für das Hashing eines Passworts und entsprechend die Zeit für das Knacken des Passworts mit Brute-Force anzupassen. Alle diese Algorithmen verwenden Salt, um sich vor Rainbow-Tabellenangriffen zu schützen.

Keiner dieser Algorithmen ist furchtbar schwach, aber es gibt einige Unterschiede:

  • bcrypt gibt es seit fast 20 Jahren, es ist weit verbreitet und hat den Test der Zeit bestanden. Es ist ziemlich resistent gegen GPU-Angriffe, aber nicht gegen FPGA
  • Argon2 ist das neueste Produkt und hat den Password Hashing-Wettbewerb 2015 gewonnen. Es bietet einen besseren Schutz gegen GPU- und FPGA-Angriffe, ist aber etwas zu neu für meinen Geschmack
  • Ich weiß nicht viel über Verschlüsselung. Es wurde entwickelt, um GPU- und FPGA-beschleunigte Angriffe zu verhindern, aber ich habe gehört, dass es sich als nicht so stark herausgestellt hat, wie ursprünglich behauptet
  • PBKDF2 ist eine Familie von Algorithmen, die durch die verschiedenen Hash-Funktionen parametrisiert werden. Es bietet keinen spezifischen Schutz vor GPU- oder ASIC - Angriffen, insbesondere wenn eine schwächere Hash-Funktion wie SHA-1 verwendet wird, ist jedoch FIPS-zertifiziert, wenn es für Sie wichtig ist. und noch akzeptabel, wenn die Anzahl der Iterationen groß genug ist.

Basierend auf Algorithmen alleine würde ich wahrscheinlich mit bcrypt arbeiten, wobei PBKDF2 am ungünstigsten ist.

Es ist jedoch nicht die ganze Geschichte, da selbst der beste Algorithmus durch eine schlechte Implementierung unsicher gemacht werden kann. Schauen wir uns an, was für die .NET-Plattform verfügbar ist:

  • Bcrypt ist über bcrypt.net verfügbar. Sie sagen, die Implementierung basiert auf Java jBCrypt. Derzeit gibt es 6 Mitwirkende und 8 Probleme (alle geschlossen) auf Github. Insgesamt sieht es gut aus, aber ich weiß nicht, ob jemand hat Ich habe eine Überprüfung des Codes durchgeführt, und es ist schwer zu sagen, ob eine aktualisierte Version bald verfügbar sein wird, wenn eine Sicherheitsanfälligkeit gefunden wird. Ich habe gehört, dass Stack Overflow aus diesen Gründen von der Verwendung von bcrypt abgewichen ist
  • Der wahrscheinlich beste Weg zur Verwendung von Argon2 ist die Bindung an die bekannte Libsodium-Bibliothek, z. https://github.com/adamcaudill/libsodium-net . Die Idee ist, dass der größte Teil der Krypto über libsodium implementiert wird, was erhebliche Unterstützung bietet, und die "ungetesteten" Teile sind ziemlich begrenzt. In der Kryptographie bedeuten Details jedoch eine Menge. In Kombination mit Argon2, das relativ neu ist, würde ich dies als experimentelle Option betrachten
  • .NET hatte lange Zeit eine integrierte Implementierung von PBKDF2 über die Klasse Rfc2898DeriveBytes . Die Implementierung kann jedoch nur die SHA-1-Hash-Funktion verwenden, die heutzutage als zu schnell angesehen wird, um sicher zu sein
  • Schließlich ist die neueste Lösung Microsoft.AspNetCore.Cryptography.KeyDerivation das über NuGet verfügbare Paket. Es bietet PBKDF2-Algorithmus mit SHA-1-, SHA-256- oder SHA-512-Hash-Funktionen, was erheblich besser ist als Rfc2898DeriveBytes. Der größte Vorteil hierbei ist, dass die Implementierung von Microsoft bereitgestellt wird. Ich kann zwar die kryptografische Sorgfalt von Microsoft-Entwicklern gegenüber BCrypt.net- oder libsodium-Entwicklern nicht richtig einschätzen, aber es ist nur sinnvoll, darauf zu vertrauen, denn wenn Sie eine .NET-Anwendung ausführen, sind Sie es vertrauen bereits stark auf Microsoft. Wir können auch erwarten, dass Microsoft Updates veröffentlicht, wenn Sicherheitsprobleme festgestellt werden. Hoffnungsvoll.

Zusammenfassend lässt sich sagen, dass PBKDF2 zwar der am wenigsten bevorzugte Algorithmus der vier ist, die Verfügbarkeit der von Microsoft bereitgestellten Implementierung dies jedoch übertrifft. Daher wäre die vernünftige Entscheidung die Verwendung von Microsoft.AspNetCore.Cryptography.KeyDerivation.

Das aktuelle Paket zielt derzeit auf .NET Standard 2.0 ab, das in .NET Core 2.0 oder .NET Framework 4.6.1 oder höher verfügbar ist. Wenn Sie eine frühere Framework-Version verwenden, können Sie die frühere Version des Pakets 1.1. verwenden, die auf .NET Framework 4.5.1 oder .NET Core 1.0 abzielt. Leider ist es nicht möglich, es in noch früheren Versionen von .NET zu verwenden.

Die Dokumentation und das Arbeitsbeispiel finden Sie unter docs.Microsoft.com . Kopieren Sie es jedoch nicht so, wie es ist. Es gibt noch Entscheidungen, die ein Entwickler treffen muss.

Die erste Entscheidung ist, welche Hash-Funktion verwendet werden soll. Zu den verfügbaren Optionen gehören SHA-1, SHA-256 und SHA-512. Von diesen ist SHA-1 definitiv zu schnell, um sicher zu sein, SHA-256 ist anständig, aber ich würde SHA-512 empfehlen, da es angeblich aufgrund der Verwendung von 64-Bit-Operationen schwieriger ist, von GPU-basierten Angriffen zu profitieren.

Dann müssen Sie die Länge der Passwort-Hash-Ausgabe und die Länge des Salt-Werts auswählen. Es ist nicht sinnvoll, die Ausgabe länger als die Ausgabe der Hash-Funktion zu halten (z. B. 512 Bit für SHA-512), und es wäre wahrscheinlich am sichersten, wenn Sie genau so arbeiten würden. Für die Salzlänge gehen die Meinungen auseinander. 128 Bit sollten ausreichen, aber in jedem Fall bietet die Länge, die länger als die Länge der Hash-Ausgabe ist, sicherlich keine Vorteile.

Als nächstes gibt es eine Iterationszahl. Je größer es ist, desto schwieriger ist es, Passwort-Hashes zu knacken, aber desto länger dauert es, sich anzumelden. Ich würde vorschlagen, es so zu wählen, dass das Hashing auf dem typischen Produktionssystem 0,25 - 1 Sekunden dauert, und auf jeden Fall sollte nicht weniger als 10000 sein.

Normalerweise würden Sie ein Byte-Array als Salt- und Hash-Werte erhalten. Verwenden Sie Base64, um sie in Zeichenfolgen zu konvertieren. Sie können zwei verschiedene Spalten in der Datenbank verwenden oder Salt und Kennwort in einer Spalte mithilfe eines Trennzeichens kombinieren, das in Base64 nicht vorhanden ist.

Vergessen Sie nicht, einen Kennwort-Hashing-Speicher so einzurichten, dass Sie in Zukunft nahtlos zu einem besseren Hashing-Algorithmus wechseln können.

2
ovolko

PBKDF2 verwendet HMACSHA1 ......., wenn Sie eine modernere HMACSHA256- oder HMACSHA512-Implementierung wünschen und dennoch eine Schlüsselverlängerung wünschen, um den Algorithmus langsamer zu machen. Ich empfehle diese API: https://sourceforge.net/projects/pwdtknet /

2
thashiznets

Bitte benutze dies, da ich die gleichen Probleme habe wie vorher, aber es könnte das kleine Code-Snippet lösen

    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

Die Klasse System.Security.Cryptography.SHA256 sollte den Trick ausführen:

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

1
James Kovacs