FIG-065

パスワードはなぜソルトを足すのか

暗号 2026.06.26 公開 読了 約11分

パスワードをそのまま(平文で)保存するのは論外です。ではハッシュ化して保存すれば安全か――というと、それだけでは足りません。攻撃者は「よくあるパスワード」を片っ端からハッシュ化した早見表(レインボーテーブル)を持っていて、漏れたハッシュを逆引きできるからです。

そこで効くのがソルト。ユーザーごとに違うランダムな文字列をパスワードに混ぜてからハッシュします。下の図1で、ソルトをON/OFFして、レインボーテーブルでの逆引きが成功するか失敗するかを確かめてください(ハッシュはあなたのブラウザが計算する本物のSHA-256です)。

STORED HASH — データベースに保存される値
SHA-256( password123 )
計算中…
RAINBOW TABLE — 攻撃者が持つ「よくあるパスワード → ハッシュ」早見表
123456
password
qwerty
password123
letmein
パスワードを入力すると、逆引きできるか判定します。
図1 — ソルトを足すと、同じパスワードでもハッシュが変わり、早見表では逆引きできない

ソルトは「早見表」を無力化する

無塩(ソルトなし)のハッシュには、2つの弱点があります。1つ目は同じパスワードが必ず同じハッシュになること。だから1つ逆引きできれば、同じパスワードを使う全員が芋づる式にバレます。2つ目は事前計算が効くこと。攻撃者はよく使われるパスワードを前もってハッシュ化した巨大な表を用意でき、漏れたハッシュを照合するだけで一致を見つけられます。図1で「ソルトなし」のまま password123 を入れると、表の中に同じハッシュが見つかって逆引きが成功してしまいます。

ソルトを足すと、保存されるのは SHA-256(salt + password) になります。ソルトはユーザーごとに違うので、たとえ2人が同じ password123 を使っても、保存されるハッシュはまったくの別物。攻撃者の早見表は「無塩のハッシュ」しか載っていないので一切一致しません。図1の「別ユーザー」ボタンでソルトを変えると、同じパスワードなのにハッシュが丸ごと変わるのが見えます。ソルトは秘密にする必要はなく、ハッシュと一緒に保存して構いません――目的は事前計算と使い回しの検出を防ぐことだからです。

もう一段の守りがストレッチングです。ハッシュを何万回も繰り返しかけて、1回の計算をわざと重くします。正規ログインは1回だけなので体感ゼロですが、総当たり攻撃は1個試すたびに同じコストがかかり、現実的な時間で破れなくなります。bcryptArgon2 は、このソルトとストレッチングを最初から組み込んだ、パスワード保管の定番です。

用語ミニ辞書
ソルト
パスワードに混ぜるユーザーごとのランダム値。同じパスワードでも別ハッシュにする。秘密でなくてよい。
レインボーテーブル
よくあるパスワードとそのハッシュの対応表。無塩ハッシュの逆引きに使われる。
ストレッチング
ハッシュを何万回も繰り返して計算を重くし、総当たりを非現実的にする手法。
bcrypt / Argon2
ソルトとストレッチングを内蔵したパスワード用ハッシュ。SHA単体より保管に適する。

まとめ

パスワードは平文で持たず、ハッシュで保存する。ただしハッシュだけでは早見表で逆引きされるので、ユーザーごとのソルトで同一性と事前計算を断ち切り、ストレッチングで総当たりを遅くする――この3点セットが基本です。実装では自前のSHAをこねるのではなく、bcryptArgon2 を使えば、これらがまとめて手に入ります。図1でソルトをON/OFFして、逆引きの成否がひっくり返る瞬間を確かめてください。