3秒でわかる
本人だけが知る文字列で認証する仕組み。保管側は元に戻せない形へ変換して持ち、漏洩しても本人になりすまされないようにする必要があります。
30秒図解
もう少し詳しく
どういうものか
パスワードは、本人だけが知っている文字列を示すことで本人だと確かめる認証方式です。仕組みは単純ですが、保管する側の責任が重い点が特徴です。入力された文字列をそのままデータベースに置く実装は、漏洩した瞬間に全利用者のなりすましを許します。
現在の標準は、パスワードをハッシュ関数で変換した値だけを保存する方式です。ハッシュは一方向で、結果から元の文字列を復元できません。認証のたびに入力値を同じ手順で変換し、保存済みの値と一致するかだけを見ます。
なぜ必要か
利用者は同じパスワードを複数のサービスで使い回します。あるサイトの生パスワードが流出すると、被害はそのサイトに留まらず、同じ組み合わせで他のサービスへログインを試す攻撃に直結します。保管側が復元できない形で持つことは、自社の事故を利用者の他サービスに波及させないための最低限の配慮です。
具体例
Node.js で bcrypt を使う場合の登録と照合です。
import bcrypt from "bcrypt";
// 登録時。salt は自動で生成され、結果の文字列に含まれる
const hash = await bcrypt.hash(plainPassword, 12);
await db.users.insert({ email, passwordHash: hash });
// ログイン時。生の値を比べず、照合<a href="/glossary/function" class="text-primary font-medium underline underline-offset-2 hover:text-primary-dark">関数</a>に任せる
const user = await db.users.findByEmail(email);
const ok = user ? await bcrypt.compare(plainPassword, user.passwordHash) : false;第二引数の 12 はコストで、大きいほど計算が重くなり、総当たりの速度を落とせます。
つまずきやすいところ
汎用のハッシュ関数を選んでしまうのが典型的な誤りです。SHA-256 は高速に設計されているため、専用機材を使えば一秒に数十億回試せます。パスワードには、意図的に遅く作られた bcrypt、scrypt、Argon2 を使います。
もう一つ、利用者ごとに異なる salt が要ります。salt が無いと同じパスワードが同じハッシュになり、あらかじめ計算した対応表で一気に破られます。bcrypt は salt を内部で生成して結果に含めるため、自分で管理する必要はありません。
エラーの返し方にも注意が要ります。存在しないメールアドレスと誤ったパスワードで異なるメッセージを返すと、登録済みのアドレスを外部から特定できてしまいます。定期変更の強制は、単純な文字列への退化を招くため現在は推奨されていません。長さを確保することと、二要素認証を併用することのほうが効果があります。
覚え方
保管側は「合っているかどうかだけ判定できて、中身は読めない」形で持つ。これが守れていれば設計の大半は正しくなります。