Javaでハッシュ値を使ったパスワード照合の仕組みと実装方法
こんにちは。ゆうせいです。
今回は「ハッシュ値を使ってパスワードの照合をする方法」について、丁寧にわかりやすく説明していきます。
前回はPBKDF2などを使って“安全にハッシュ化する方法”を学びましたね。でも、それだけでは終わりませ。実際のアプリケーションでは、「ログイン時に正しいパスワードかどうかをどうやって判断するのか?」という部分がとても大事です。
パスワード照合とは?
照合とは、ユーザーが入力したパスワードが、保存されたハッシュ値と一致するかをチェックする処理です。
ポイントはこうです:
「ハッシュ値は復号できないけど、同じ入力からは同じハッシュが得られる」
この性質を使って、ユーザーが入力したパスワードをもう一度同じ条件でハッシュ化し、保存された値と一致するか比較するわけです。
照合の流れ(図で理解)
[ユーザー入力パスワード]
↓
[ソルトを付加する]
↓
[PBKDF2でハッシュ化]
↓
[保存されているハッシュと比較]
↓
一致?→ログイン成功!
不一致→エラー表示
Javaでの実装例(照合処理)
以下に、ハッシュ値とソルトを使ってパスワード照合を行う実装例を紹介します。
前提:ユーザー登録時に「ソルト」と「ハッシュ値」をDBに保存済み。
パスワード照合のコード例(PBKDF2)
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.util.Base64;
public class PasswordVerifier {
public static boolean verifyPassword(String inputPassword, String storedHashBase64, String storedSaltBase64, int iterations, int keyLength) throws Exception {
byte[] salt = Base64.getDecoder().decode(storedSaltBase64);
byte[] storedHash = Base64.getDecoder().decode(storedHashBase64);
PBEKeySpec spec = new PBEKeySpec(inputPassword.toCharArray(), salt, iterations, keyLength);
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] inputHash = skf.generateSecret(spec).getEncoded();
// ハッシュの比較
if (inputHash.length != storedHash.length) return false;
// タイミング攻撃対策として一定時間かけて比較
int diff = 0;
for (int i = 0; i < inputHash.length; i++) {
diff |= inputHash[i] ^ storedHash[i];
}
return diff == 0;
}
}
実際の照合コードの使い方
String inputPassword = "P@ssw0rd123";
String storedSalt = "ユーザー登録時に保存したソルト(Base64)";
String storedHash = "ユーザー登録時に保存したハッシュ(Base64)";
int iterations = 65536;
int keyLength = 256;
boolean match = PasswordVerifier.verifyPassword(inputPassword, storedHash, storedSalt, iterations, keyLength);
System.out.println(match ? "ログイン成功!" : "パスワードが違います");
注意:なぜBase64が必要なの?
ハッシュやソルトはバイナリデータなので、そのまま文字列として保存すると文字化けします。そこで登場するのがBase64。
Base64は、バイナリデータを文字列として安全に保存・送信するためのエンコード方法です。
比較処理にも落とし穴がある!
storedHash.equals(inputHash);
このような単純な比較は、タイミング攻撃(Timing Attack)のリスクがあります。
安全な実装では、すべてのバイトを最後まで見て、差分を合成するような書き方にしましょう(先ほどの diff |=
方式)。
DBに保存する値の構成例
項目 | 内容例 |
---|---|
ソルト | kJ3v93fA7l9a+3DQx7+QvA== |
ハッシュ | 2r4S9P5F6dJQq7F7N1eIDoZr… |
回数 | 65536 |
長さ | 256 |
アルゴリズム | PBKDF2WithHmacSHA256 |
このように、ハッシュ値だけでなくパラメータも保存しておくと、将来の照合時に困りません。
まとめ
- パスワード照合では、再ハッシュして一致確認する
- 同じソルト・同じ回数・同じアルゴリズムが必須条件
- 比較は安全な方法(diff |=)で行うのが理想
- Base64を使ってソルト・ハッシュを文字列で保存しよう
次のステップは?
- パスワード登録時の処理と照合処理を統一的に設計する
- Spring Securityなどのフレームワークでの統合も学ぶ
- bcryptなど他のKDFとの比較と導入検討も進めよう
セイ・コンサルティング・グループの新人エンジニア研修のメニューへのリンク
投稿者プロフィール
