44

アプリケーションの登録フォームを作成していますが、C# を初めて使用するという問題がまだあります。

パスワードをmd5またはsha-256、できればsha-256に暗号化/ハッシュすることを検討しています。

良い例はありますか?「string password;」から情報を取得できるようにしたい それをハッシュして、変数「string hPassword;」に格納します。何か案は?

4

9 に答える 9

79

単純なハッシュやソルト ハッシュを使用しないでください。bcrypt (ここでは .NET 実装を使用) やPBKDF2 (組み込み実装を使用)などのキー強化手法を使用します。

PBKDF2 を使用した例を次に示します。

パスワードからキーを生成するには...

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
}

そして、パスワードが有効かどうかをテストするには...

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!");
}
于 2010-12-02T00:33:16.610 に答える
55

System.Security.Cryptography名前空間を使用する必要があります。具体的には、MD5classまたはSHA256class .

このページのコードから少し引き出して、両方のクラスが同じ基本クラス ( HashAlgorithm) を持っていることを知っていれば、次のような関数を使用できます。

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

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

   return BitConverter.ToString(hashedBytes);
}

次に、次のように呼び出すことができます (MD5 の場合):

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

または SHA256 の場合:

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

編集: Salt サポートの追加
dtb がコメントで指摘したように、このコードにsaltを追加する機能が含まれていれば、このコードはより強力になります。ご存じない方のために説明すると、salt はハッシュ関数への入力として含まれるランダム ビットのセットであり、ハッシュ化されたパスワードに対する辞書攻撃 (たとえば、rainbow tableを使用) を阻止するのに大いに役立ちます。ComputeHashこれは、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);
}

これがお役に立てば幸いです!

于 2010-12-01T22:45:59.320 に答える
6

パスワードをデータベースに保存するときは、ハッシュする前に常にパスワードをソルトする必要があります。

推奨されるデータベース列:

  • PasswordSalt : int
  • パスワードハッシュ : バイナリ(20)

オンラインで見つけたほとんどの投稿では、ソルトとハッシュの ASCII エンコードについて説明していますが、それは必要なく、不要な計算を追加するだけです。また、SHA-1を使用する場合、出力は 20 バイトしかないため、データベース内のハッシュ フィールドの長さは 20 バイトで十分です。SHA-256 についてのご質問は理解できますが、やむを得ない理由がない限り、ほとんどのビジネス プラクティスでは SHA-1 とソルト値を使用するだけで十分です。SHA-256 に固執する場合、データベースのハッシュ フィールドの長さは 32 バイトである必要があります。

以下は、ソルトを生成し、ハッシュを計算し、パスワードに対してハッシュを検証するいくつかの関数です。

以下のソルト関数は、暗号的に作成された 4 つのランダム バイトから、暗号的に強力なソルトを整数として生成します。

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

次に、以下の関数でソルトを使用してパスワードをハッシュできます。ソルトはパスワードに連結され、ハッシュが計算されます。


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

パスワードのチェックは、ハッシュを計算し、予想されるハッシュと比較するだけで実行できます。


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

    return hashedPassword.SequenceEqual(correctPasswordHash);
}

于 2010-12-02T03:12:47.100 に答える
2

これは、持続性を認識しない SecuredPassword クラスの完全な実装です。

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

そしてテスト:

 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);
    }
}
于 2013-12-06T15:06:40.523 に答える
2

ハッシュ化されたパスワードを保存する場合は、SHA-256 の代わりにbcryptを使用してください。問題は、SHA-256 が速度を重視して最適化されているため、誰かがデータベースにアクセスした場合に、パスワードに対するブルート フォース攻撃が容易になることです。

この記事を読んでください:レインボー テーブルで十分: 安全なパスワード スキームについて知っておくべきことと、以前の SO の質問に対するこの回答。

記事からの引用:

問題は、MD5 が高速であることです。SHA1 や SHA256 などの最新の競合他社も同様です。ハッシュはほとんどすべての暗号システムの構成要素であり、通常はパケットごとまたはメッセージごとに要求実行されるため、速度は最新の安全なハッシュの設計目標です。

速度は、パスワード ハッシュ関数で望ましくないものです。


最後に、パスワードを安全に保存するには、PHK の MD5 スキーム、Provos-Maziere の Bcrypt スキーム、および SRP の 3 つの合理的なオプションがあることを学びました。正しい選択は Bcrypt であることがわかりました。

于 2010-12-01T22:51:04.710 に答える
2

PBKDF2 は HMACSHA1 を使用しています.......もっと最新の HMACSHA256 または HMACSHA512 の実装が必要で、アルゴリズムを遅くするためにキーストレッチが必要な場合は、この API をお勧めします: https://sourceforge.net/projects/pwdtknet/

于 2012-10-04T04:05:36.270 に答える
1

System.Security.Cryptography.SHA256 クラスがそのトリックを行う必要があります。

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

于 2010-12-01T22:47:01.930 に答える