WEBサービスなどユーザーのログイン処理を作成する場合、各ユーザーのパスワードをデータベースなどに保存する必要があります。
このとき、パスワードは正しい方法で暗号化しておく必要があります。
さらに、複数のユーザーが同じパスワードを設定している際にまったく同じ値が保存されていると、パスワードを推測される可能性があります(これはソルトを付加することで対策します)。
また、機械的な総当たり攻撃への対策も必要になります(これはストレッチング(暗号化の繰り返し)という方法で対策します)。
では、実際にどのような方法で暗号化すれば良いかというと、.NET Framework ではRfc2898DeriveBytesクラスを使用した暗号キー(PBKDF2)をお勧めします。ソルトやストレッチングに対応しているため、実装自体は非常に簡単です。
public static byte[] CreateSalt(int size) { var bytes = new byte[size]; using (var rngCryptoServiceProvider = new RNGCryptoServiceProvider()) { rngCryptoServiceProvider.GetBytes(bytes); } return bytes; }
private static byte[] CreatePBKDF2Hash(string password, byte[] salt, int size, int iteration) { using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt, iteration)) { return rfc2898DeriveBytes.GetBytes(size); } }
int saltSize = 32; // ソルトのサイズ int hashSize = 32; // ハッシュサイズ int iteration = 10000; // ストレッチングの回数 // ソルトを生成する byte[] saltBytes = CreateSalt(saltSize); // PBKDF2によるハッシュを生成 byte[] hashBytes = CreatePBKDF2Hash("p@ssword", saltBytes, hashSize, iteration); // Base64 文字列に変換 string saltText = Convert.ToBase64String(saltBytes); string hashText = Convert.ToBase64String(hashBytes); // データベース等に salt, hash, iteration を保存しておく
ログイン処理などで認証を行う際は入力された文字列と保存していたsaltとiterationを使用してハッシュを生成して比較します(iterationはある程度大きな値で固定でも良いかもしれません)。
// 認証の際は入力された文字列と保存していたsaltを使用してハッシュを生成 byte[] inputHashBytes = CreatePBKDF2Hash("p@ssword", saltBytes, hashSize, iteration); string inputHashText = Convert.ToBase64String(inputHashBytes); // 保存していたハッシュと入力文字から生成したハッシュを比較して認証を行う if (hashText == inputHashText) { // ログイン処理 }