3秒でわかる
入力を固定長の値へ一方向に変換する処理。元に戻せない性質を使い、パスワードを預かりながら平文では持たない設計を作れます。
もう少し詳しく
どういうものか
任意の長さのデータを、決まった長さの値へ変換する計算がハッシュ化です。同じ入力からは必ず同じ結果が出ますが、結果から元の入力を計算で復元することはできません。この不可逆性が暗号化との決定的な違いです。
入力が1文字違えば結果はまったく別の値になります。この性質のおかげで、ファイルが途中で壊れていないかの検証にも使われます。
なぜ必要か
パスワードをそのまま保存したデータベースが流出すると、全利用者のパスワードがその場で漏れます。他サービスで同じパスワードを使い回している人も巻き添えになります。ハッシュ値だけを保存しておけば、流出しても元のパスワードは分かりません。
ログイン時は、入力されたパスワードを同じ手順でハッシュ化し、保存済みの値と一致するかを比べます。サーバーは一度も平文を持たずに本人確認ができます。
具体例
import bcrypt
# 登録時 ハッシュ化して保存する
password = "himitsu-2026"
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt())
print(hashed) # b'$2b$12$...' <a href="/glossary/salt" class="text-primary font-medium underline underline-offset-2 hover:text-primary-dark">ソルト</a>を含んだ文字列
# ログイン時 入力と保存値を照合する
print(bcrypt.checkpw("himitsu-2026".encode(), hashed)) # True
print(bcrypt.checkpw("wrong".encode(), hashed)) # Falseつまずきやすいところ
SHA-256 や MD5 をそのまま使うのは、パスワード保管では不適切です。これらは高速に計算できるよう作られており、攻撃者が1秒間に膨大な候補を試せてしまいます。パスワードには bcrypt や Argon2 のように、あえて遅く、計算量を調整できる関数を使います。
ソルトを付けない実装も危険です。ソルトは利用者ごとに変える追加のランダム値で、これが無いと同じパスワードの人が同じハッシュ値になり、あらかじめ計算した対応表 (レインボーテーブル) で一気に破られます。bcrypt はソルトを自動で生成し、結果の文字列に含めて返します。
照合を == で書くのも避けます。一致するまでの時間差から情報が漏れるため、専用の比較関数を使います。
似た用語との違い
| 手法 | 元に戻せるか | 目的 |
|---|---|---|
| ハッシュ化 | 戻せない | パスワード保管、改ざん検知 |
| 暗号化 | 鍵があれば戻せる | 通信内容や保存データの秘匿 |
| エンコード | 誰でも戻せる | データ形式の変換 (Base64 など) |
Base64 を「暗号化しました」と説明しているコードを見かけますが、あれは変換であって秘匿にはなりません。