web-dev-qa-db-ja.com

MD5またはsha-256 C#を使用したパスワードのハッシュ化

私はアプリケーション用の登録フォームを書いていますが、c#が初めてであることにまだ問題があります。

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

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

43
Sean

単純なハッシュ、またはソルトハッシュを使用しないでください。 bcrypt (ここでは 。NET実装 )または PBKDF2built-実装中 )。

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!");
}
78
LukeH

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

このページ のコードから少し引き出して、両方のクラスが同じ基本クラス( 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());

編集:塩サポートの追加
dtbがコメントで指摘したように、このコードは salt を追加する機能が含まれていればより強力になります。あなたがそれに慣れていないなら、ソルトはハッシュ関数への入力として含まれるランダムなビットのセットであり、ハッシュされたパスワードに対する辞書攻撃を阻止するのに大いに役立ちます(例えば、 Rainbowテーブル )。以下は、saltをサポートするComputeHash関数の修正バージョンです。

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

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

53
Donut

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

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

  • PasswordSalt:int
  • PasswordHash:バイナリ(20)

オンラインで見つけたほとんどの投稿では、ASCII saltとハッシュのエンコードについて説明しますが、これは不要であり、不要な計算を追加するだけです。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);
}

5
Adam Spicer

以下は、永続性を意識しない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);
    }
}
2
Sam Shiles

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

この記事を読む: レインボーテーブルで十分:安全なパスワードスキームについて知っておくべきこと そしてこれ answer 前のSO質問。

記事からの引用:

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

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


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

2
Jeff Ogata

TL; DRは Microsoft.AspNetCore.Cryptography.KeyDerivation を使用し、SH-512でPBKDF2を実装します。

パスワードハッシュを開始するのは、 OWASPガイドライン の内容を確認することです。推奨されるアルゴリズムのリストには、Argon2、PBKDF2、scrypt、およびbcryptが含まれます。これらすべてのアルゴリズムを調整して、パスワードのハッシュにかかる時間と、それに応じてブルートフォースでパスワードを解読する時間を調整できます。これらのアルゴリズムはすべてソルトを使用して、レインボーテーブル攻撃から保護します。

これらのアルゴリズムはどちらもそれほど強力ではありませんが、いくつかの違いがあります。

  • bcryptは20年近く存在し、広く使用されており、時の試練に耐えてきました。 GPU攻撃にはかなり耐性がありますが、FPGAには耐性がありません
  • Argon2は最新の追加製品であり、2015年のパスワードハッシュコンペティションで優勝しています。 GPUやFPGA攻撃に対する保護は強化されていますが、私の好みには少し近すぎます
  • 私はscryptについてあまり知りません。 GPUおよびFPGAで加速される攻撃を阻止するように設計されていますが、当初の主張ほど強力ではないことがわかった
  • PBKDF2は、さまざまなハッシュ関数によってパラメータ化されたアルゴリズムのファミリです。特にSHA-1などのより弱いハッシュ関数が使用される場合、GPUまたはASIC攻撃に対する特定の保護を提供しませんが、あなたにとって重要な場合はFIPS認定されていますが、繰り返しの数が十分に大きい場合でも受け入れられます。

アルゴリズムのみに基づいて、おそらくbcryptを使用しますが、PBKDF2は最も好ましくありません。

しかし、それは完全な話ではありません。最良のアルゴリズムでさえ、実装が悪いと安全ではなくなる可能性があるからです。 .NETプラットフォームで利用可能なものを見てみましょう。

  • Bcryptは bcrypt.net で利用できます。実装はJava jBCryptに基づいています。現在、GitHubには6つの貢献者と8つの問題(すべてクローズ))があります。コードの監査を行い、脆弱性が見つかった場合に更新されたバージョンがすぐに利用可能になるかどうかを判断するのは困難です。
  • おそらく、Argon2を使用する最良の方法は、よく知られているlibsodiumライブラリへのバインディングを使用することです。 https://github.com/adamcaudill/libsodium-net 。暗号化の大部分はlibsodiumを介して実装され、かなりのサポートがあり、「テストされていない」部分はかなり限られているという考え方です。ただし、暗号化の詳細には多くの意味があるため、比較的最近のArgon2と組み合わせて、実験的なオプションとして扱います
  • 長い間、.NETには Rfc2898DeriveBytes クラスを介したPBKDF2の実装が組み込まれていました。ただし、実装ではSHA-1ハッシュ関数のみを使用できます。SHA-1ハッシュ関数は、今日では安全であるには速すぎると見なされています
  • 最後に、最新のソリューションは Microsoft.AspNetCore.Cryptography.KeyDerivation NuGetを介して利用可能なパッケージです。 SHA-1、SHA-256、またはSHA-512ハッシュ関数を備えたPBKDF2アルゴリズムを提供し、Rfc2898DeriveBytes。ここでの最大の利点は、実装がMicrosoftによって提供されることであり、BCrypt.netまたはlibsodium開発者に対するMicrosoft開発者の暗号の勤勉性を適切に評価することはできませんが、.NETアプリケーションを実行している場合、すでにマイクロソフトに大きく依存しています。また、セキュリティの問題が見つかった場合、マイクロソフトが更新プログラムをリリースすることを期待する場合があります。うまくいけば。

これまでの研究を要約すると、PBKDF2は4つのアルゴリズムのうち最も優先度が低いかもしれませんが、Microsoftが提供する実装の可用性はそれを上回っているため、合理的な決定はMicrosoft.AspNetCore.Cryptography.KeyDerivation

現在、最近のパッケージは.NET Standard 2.0を対象としているため、.NET Core 2.0または.NET Framework 4.6.1以降で使用できます。以前のフレームワークバージョンを使用している場合、.NET Framework 4.5.1または.NET Core 1.0を対象とする以前のバージョンのパッケージ 1.1. を使用できます。残念ながら、以前のバージョンの.NETでも使用することはできません。

ドキュメントと実例は docs.Microsoft.com で入手できます。ただし、そのままコピーアンドペーストしないでください。開発者が行う必要がある決定がまだあります。

最初の決定は、使用するハッシュ関数です。利用可能なオプションには、SHA-1、SHA-256、およびSHA-512があります。そのうち、SHA-1はセキュリティが確保するには速すぎることは間違いありません。SHA-256はまともですが、64ビット操作を使用するとGPUベースの攻撃の恩恵を受けにくくなるため、SHA-512をお勧めします。

次に、パスワードハッシュ出力の長さとソルトの長さを選択する必要があります。ハッシュ関数の出力(SHA-512の場合は512ビットなど)よりも長く出力することは理にかなっておらず、そのようにするのがおそらく最も安全でしょう。塩の長さについては、意見が異なります。 128ビットで十分ですが、いずれにしても、ハッシュ出力の長さよりも長い長さは確かに利点を提供しません。

次に、反復カウントがあります。大きければ大きいほど、パスワードハッシュを解読するのが難しくなりますが、ユーザーのログインに時間がかかります。典型的な運用システムでは、ハッシュが0.25〜1秒かかるように選択することをお勧めします。 10000未満であってはなりません。

通常、バイト配列はソルト値とハッシュ値として取得します。 Base64を使用して文字列に変換します。データベースで2つの異なる列を使用するか、Base64にはないセパレータを使用して1つの列でsaltとパスワードを組み合わせることができます。

将来、より良いハッシュアルゴリズムにシームレスに移行できるように、パスワードハッシュストレージを考案することを忘れないでください。

2
ovolko

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

2
thashiznets

私は以前と同じ問題を抱えていますが、それを解決できる可能性があるため、これを使用してください

    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

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

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

1
James Kovacs