パスワードのハッシュ化で必ず出てくる「ソルト」。ハッシュ化の前にランダムな値を1つ加えるだけで、安全性が大きく変わります。仕組みと注意点を解説します(攻撃手順は載せません)。
なぜソルトで安全になるのか
ソルトなしだと、同じパスワードの人は保存されるハッシュも同じになります。これにより、攻撃者は2つの方法を使えます。事前計算した対応表(レインボーテーブル)でそのまま元のパスワードを調べること、そして1人分の解析結果を同じパスワードの全員に使い回すことです。
ソルトを足すと、同じ "password123" でも保存値が利用者ごとにまったく別物になります。
同じ理屈で、ソルトは利用者ごと(できればパスワードごと)に新しく作る必要があります。全員で同じソルトを使い回すと、同じパスワードの人どうしの保存値がまた一致してしまい、せっかくの効果が薄れます。生成には、推測されにくい暗号学的な乱数を使い、作ったソルトはハッシュ値と一緒に保存します(秘密にする必要はありません)。
なお、ソルトが守るのは「事前計算した表」と「使い回しの一括解析」までです。1つのパスワードを総当たりで狙う攻撃そのものを遅くするのはソルトの仕事ではなく、bcrypt/Argon2 のようなわざと遅いハッシュが担います。両者は守備範囲が違うので、必ずセットで使います。
ソルトの長さと作り方
基準になる文書は次の2つです。
- NIST SP 800-63B-4(米国の認証ガイドライン):ソルトは32ビット以上で、保存済みのソルトどうしが重なりにくいように選ぶ。ソルトとハッシュ値は、パスワードごとに両方保存する
- RFC 9106(Argon2の仕様):パスワード用のソルトは16バイト(128ビット)を推奨。容量に制約がある場合は64ビットまで減らせる
作り方で守ることは3つです。
- 暗号学的な乱数で作る:言語の安全な乱数関数を使います。
Math.random()やPHPのmt_rand()のような、予測できる乱数は使いません - 推測できる値を使わない:ユーザー名、メールアドレス、ユーザーID、登録日時はソルトの代わりになりません
- パスワードを変えたら、ソルトも作り直す:古いソルトを使い続けると、新旧のハッシュを結びつける手がかりが残ります
実際には、bcrypt・Argon2・scrypt の標準実装がこれらを自動で満たします。自分でソルトを作るコードを書いているなら、それ自体を見直す合図です。
ソルト・ペッパー・ストレッチングの違い
パスワード保存では、似た名前の対策が3つ出てきます。守る相手が違うので、どれかがほかの代わりにはなりません。
| ソルト | ペッパー | ストレッチング(低速化) | |
|---|---|---|---|
| 何か | 利用者ごとのランダムな値 | 全体に共通の秘密の値 | ハッシュ計算をわざと重くすること |
| 秘密にするか | しない(ハッシュと一緒に保存) | する(DBとは別の場所に保管) | 設定値は秘密でなくてよい |
| 防ぐもの | 事前計算した表、同じパスワードの一括解析 | DBだけが漏れたときの解析 | 1件ずつの総当たり |
| 実装 | bcrypt/Argon2が自動で付ける | 必要に応じて自分で追加 | bcryptのコスト、Argon2のメモリ・反復回数 |
NIST SP 800-63B-4 は、ペッパーにあたる「検証側だけが知る秘密鍵」をパスワードのハッシュとは別に保管すること(できればHSMなどの専用ハードウェア内)を推奨しています。
具体例:ソルトなしの保存が問題になったLinkedIn(2012年)
2012年6月、LinkedInの利用者のものとされる約650万件のパスワードのハッシュ値がネット上に公開されたと報じられました。LinkedInは、流出したものの一部が自社アカウントのパスワードに該当することを認め、該当する利用者のパスワードを無効にしました。
同社は公式ブログで、パスワードを「ハッシュ化するだけの方式」から「ハッシュ化とソルトの両方を行う方式」へ移行を進めていたと説明しています。当時の報道では、同社がパスワードの保護にSHA-1によるハッシュ化だけを使っていたことが専門家から批判されました。
この事例から分かることは2つです。
- ソルトなしのハッシュは、よくあるパスワードほど事前計算の表で一度に戻される
- 移行を始めていても、古い方式のハッシュが残っていれば、漏れたときの被害はその方式で決まる
自分の環境で確認する方法
保存されている文字列の形を見る
自分が管理する開発環境のデータベースで、パスワード欄を数件確認します。bcrypt なら $2b$12$ のあとの22文字がソルトです。Argon2 なら $argon2id$v=19$m=…,t=…,p=…$ のあとに「ソルト」と「ハッシュ」が $ で区切られて並びます。どちらの形でもなく、別の列にもソルトが見当たらない場合は、ソルトなしで保存されている可能性があります。
同じパスワードで2つのテスト用アカウントを作る
開発環境で、同じパスワードのテスト用アカウントを2つ登録し、保存された値を比べます。値が一致したら、ソルトが効いていません。パスワードを変更したときにソルト部分も変わるかも、あわせて確認します。
ソルトを自分で作っているコードを探す
コード内で salt という語や、乱数・ハッシュ関数を手で組み合わせている箇所を探します。予測できる乱数、ユーザー名やIDの流用、固定文字列が見つかったら、言語やフレームワークの標準パスワード関数に置き換えます。
移行の方法は実践ガイドで確認する
ソルトなしの保存が見つかったときの移行手順(ログイン時の再ハッシュなど)と、方式ごとの推奨設定値は、パスワードの安全な保存方法にまとめています。
よくある誤解と正解
誤解
- ソルトは秘密にしないといけない
- 全ユーザー共通のソルトでいい
- ソルトを足せば弱いパスワードでも安全
正解
- ソルトは公開前提でOK(ハッシュと一緒に保存)
- 利用者ごと(できればパスワードごと)に別の乱数
- ソルトは“事前計算と使い回し解析”対策。総当たり対策は低速ハッシュが担う
当サイトの視点:まずはソルトと低速ハッシュを正しく使う
ペッパーは、データベースだけが盗まれた場合に備える追加の対策です。秘密の値を別の場所で管理する手間が増え、その値をなくすと全員のパスワードを照合できなくなります。
そのため実務では、まず bcrypt/Argon2 によるソルト+低速ハッシュを正しく使うことが先決です。ペッパーは、鍵の管理の仕組みがすでにある場合に検討します。
出典(一次情報)
- NIST「SP 800-63B-4 Digital Identity Guidelines: Authentication and Authenticator Management」(2025年8月) — pages.nist.gov(ソルトは32ビット以上、ソルトとハッシュの保存、別に保管する秘密鍵)
- RFC 9106「Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications」 — rfc-editor.org(パスワード用ソルトは16バイト推奨)
- OWASP「Password Storage Cheat Sheet」 — cheatsheetseries.owasp.org(ソルト、ペッパー、多くのライブラリがソルトを自動で扱うこと)
- PHC string format(Argon2などのハッシュ文字列の形式) — github.com/P-H-C
- TechCrunch「LinkedIn Confirms Hack And Leak Of "Some" User Passwords」(2012年6月6日) — techcrunch.com(約650万件、ハッシュ化とソルトへの移行についての同社の説明)
- Computerworld「LinkedIn provides breach update -- sort of」(2012年6月8日) — computerworld.com(SHA-1だけのハッシュ化への批判、パスワードの無効化)
次に読む
- 用語:パスワードのハッシュ化とは(ソルトと対で理解する)
- 対策:パスワードの安全な保存方法(ハッシュ化+ソルト実践)
- 入門:パスワードの正しい保管