解決策がフォートノックスでなくても、熱心にソルティングを実装する必要があります。多くの人が自分のパスワードを別の場所で再利用しており、攻撃者がクラックされたパスワード データベースを別の目的で使用することを選択した場合、侵入によって組織外にさらなる損害が生じるためです。
ソルティングは、辞書攻撃をより高価にします。使用する塩のサイズを決定することで、チャンスを微調整できます。Bruce Schneier の「Applied Cryptography」からの引用は次のとおりです。
「Salt は万能薬ではありません。Salt ビットの数を増やしてもすべてが解決するわけではありません。Salt は、パスワード ファイルに対する一般的な辞書攻撃から保護するだけであり、単一のパスワードに対する共同攻撃から保護することはできません。同じパスワードを持つ人々を保護します。複数のマシンを使用できますが、不適切なパスワードの選択が改善されるわけではありません。」
C# でのサンプルを次に示します。そんなに難しくない。また、使用するソルト サイズとハッシュ関数を選択できます。免責事項:パスワードの完全性が本当に気になる場合は、 bcryptなどを使用してください。
using System;
using System.IO;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
public class PassHash {
private static readonly RandomNumberGenerator rng = RandomNumberGenerator.Create();
public static readonly int DefaultSaltSize = 8; // 64-bit salt
public readonly byte[] Salt;
public readonly byte[] Passhash;
internal PassHash(byte[] salt, byte[] passhash) {
Salt = salt;
Passhash = passhash;
}
public override String ToString() {
return String.Format("{{'salt': '{0}', 'passhash': '{1}'}}",
Convert.ToBase64String(Salt),
Convert.ToBase64String(Passhash));
}
public static PassHash Encode<HA>(String password) where HA : HashAlgorithm {
return Encode<HA>(password, DefaultSaltSize);
}
public static PassHash Encode<HA>(String password, int saltSize) where HA : HashAlgorithm {
return Encode<HA>(password, GenerateSalt(saltSize));
}
private static PassHash Encode<HA>(string password, byte[] salt) where HA : HashAlgorithm {
BindingFlags publicStatic = BindingFlags.Public | BindingFlags.Static;
MethodInfo hasher_factory = typeof (HA).GetMethod("Create", publicStatic, Type.DefaultBinder, Type.EmptyTypes, null);
using (HashAlgorithm hasher = (HashAlgorithm) hasher_factory.Invoke(null, null))
{
using (MemoryStream hashInput = new MemoryStream())
{
hashInput.Write(salt, 0, salt.Length);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
hashInput.Write(passwordBytes, 0, passwordBytes.Length);
hashInput.Seek(0, SeekOrigin.Begin);
byte[] passhash = hasher.ComputeHash(hashInput);
return new PassHash(salt, passhash);
}
}
}
private static byte[] GenerateSalt(int saltSize) {
// This generates salt.
// Rephrasing Schneier:
// "salt" is a random string of bytes that is
// combined with password bytes before being
// operated by the one-way function.
byte[] salt = new byte[saltSize];
rng.GetBytes(salt);
return salt;
}
public static bool Verify<HA>(string password, byte[] salt, byte[] passhash) where HA : HashAlgorithm {
// OMG: I don't know how to compare byte arrays in C#.
return Encode<HA>(password, salt).ToString() == new PassHash(salt, passhash).ToString();
}
}
使用法:
新しいユーザーが資格情報を送信します。
PassHash ph = PassHash.Encode<SHA384>(new_user_password);
保存してどこかに...後で、ユーザーが再度ログインするときに、ソルトph.Salt
とph.Passhash
パスハッシュを含むユーザーレコードを検索し、次のようにします。
PassHash.Verify<SHA384>(user_login_password, user_rec.salt, user_rec.passhash)
}