【C#】ハッシュ値の計算時にソルトを付与してレインボーテーブル攻撃に備える

ハッシュ値の計算時にソルトを付与してレインボーテーブル攻撃に備える方法のまとめです。

ハッシュ値とは?

ハッシュ値とは文字列やファイルなどの同一性を検証するための値です。
全く同一の文字列/ファイルであれば全く同じハッシュ値となりますが、
文字列/ファイルの内容が少しでも異なればハッシュ値は全然別のものとなります。

具体的な用途として、例えばいまユーザが設定したパスワードをDBに保存することを考えます。
パスワードを平文のまま保存するともしハッキングされたときに大変なことになるので、
パスワード文字列のハッシュ値を計算して保存しておきます。

次回このユーザがパスワードを入力したとき、この入力されたパスワードのハッシュ値とDBに保存されているハッシュ値を比較して同一であれば正しいパスワードと判定できます。
また誤ったパスワードが入力された場合にはハッシュ値も異なるのでパスワードが不正であると判定できます。

レインボーテーブル攻撃

前節のケースでは一見セキュリティ的にうまくいくように見えますが、実は欠陥があります。

たとえばいま、平文のパスワード候補を大量に用意しておくとします。
そしてこれらのハッシュ値をすべて計算しておきます。
次に何らかの方法でDBに保存されたパスワードのハッシュ値を入手します。
これらをあらかじめ計算しておいたハッシュ値を比較し、一致すれば平文も一致するので、パスワードを入手することができます。

このような攻撃をレインボーテーブル攻撃といいます。

ソルトを付与する

レインボーテーブル攻撃に対抗するには、平文のパスワードに適当な文字列をくっつけてからハッシュ化します。
この「適当な文字列」もDBに別途保存しておき、次回のパスワード認証時には入力されたパスワードにこの文字列をくっつけてハッシュ値を求めて判定します。

この「適当な文字列」をソルトといいます。

この仕組みにより、レインボーテーブル攻撃で一致する平文が求められたとしても、
その平文はソルトが付与されたものであるため、攻撃が困難になります。

実装してみる

簡単に実装してみるとこんな感じになります。

using System.Security.Cryptography;
using System.Text;

public class Example 
{
    private void Main()
    {
        // 対象の文字列にソルトを付与したものを作成
        var targetStr = "example";
        var salt = "fjaKfaK9jvalfaok";
        var targetBytes = Encoding.UTF8.GetBytes(targetStr + salt);

        // MD5ハッシュを計算
        var csp = new MD5CryptoServiceProvider();
        var hashBytes = csp.ComputeHash(targetBytes);

        // バイト配列を文字列に変換
        var hashStr = new StringBuilder();
        foreach (var hashByte in hashBytes) {
            hashStr.Append(hashByte.ToString("x2"));
        }

        Console.WriteLine(hashStr);
    }
}

関連

light11.hatenadiary.com

参考

ja.wikipedia.org