セキュリティにおけるソルトとは何か?パスワードハッシュの説明

セキュリティにおけるソルトとは何か?パスワードハッシュの説明

2012年6月、攻撃者が1億1700万件のLinkedInアカウント情報をロシアのフォーラムに流出した。そのリストは、ソルト処理されていないSHA-1ハッシュの羅列だった。SHA-1は高速なハッシュアルゴリズムだが、同じパスワードを区別するためのソルトがないため、同じハッシュが何千回も並んでいた。セキュリティ研究者たちは約72時間以内に、ファイルの約90%を解読した。2016年5月にこの情報漏洩が1億6700万件のデータセットの一部として再び浮上すると、これらの認証情報はその後何年にもわたってクレデンシャルスタッフィング攻撃に悪用された。

この情報漏洩を些細な出来事にとどめるための防御策――保存されているすべてのパスワードに塩を加えること――は、すでに1979年には存在していた。これは「ソルト処理」と呼ばれる。目新しいものでもなく、高価でもなく、特に巧妙なものでもない。しかし、データベースからの情報漏洩が封じ込められた事件で済むか、それとも半世紀にもわたって誰もがパスワード問題に悩まされることになるかの違いを生むのだ。

現代のパスワード保存におけるほとんどの失敗は、依然として同じ根本原因に起因しています。それは、「SHA-256を使えば十分だ」と誰かが判断したことです。この記事では、ソルト処理とは何か、実際にどのように機能するのか、どのような攻撃を防ぎ、どのような攻撃を防ぐことができないのか、そしてOWASPが2026年のデフォルトとして推奨しているものについて解説します。

セキュリティとパスワードハッシュにおけるソルトとは何か

要点を簡潔にまとめると、ソルト処理とは、パスワードをハッシュ化する前にランダムなデータを追加することです。このランダムな値(ソルト)は、新しいパスワードハッシュと同時に生成される際にアカウントごとに一度だけ生成され、生成されたハッシュの横に平文で保存され、ログインのたびに読み込まれます。ソルトは鍵でも秘密でも暗号化でもありません。公開修飾子であり、その役割はただ一つ、2人のユーザーが同じパスワードを使用しても、保存されたハッシュ化されたパスワードが共有されないようにすることです。

ハッシュ化自体は一方向です。SHA-256やArgon2idにパスワードを通すと、どのアルゴリズムでも逆算できない固定長の値が得られます。ただし、「どのアルゴリズムでも」という条件には、ハッシュを事前に計算された辞書で検索するという安易な近道が一つ含まれます。ソルト処理はこの近道を防ぎます。ハッシュ化処理にソルトを加えることで、基となるパスワードのテキストが単語ごとに同一であっても、ユーザーごとに一意のハッシュが生成されます。「password」や「qwerty」のような一般的なパスワードは、データベース内で衝突しなくなります。ソルト処理によって、1行が漏洩しても、攻撃者は他のユーザーの情報を一切知ることができません。

使えるソルトと使えないソルトを区別する3つのルールがあります。1つ目のルール:すべてのパスワードには、ユーザーがサインアップまたはリセットするたびに新たに生成される独自のランダムなソルトが割り当てられます。複数のアカウントで再利用されるソルト付きパスワードは、ソルトなしパスワードとほとんど変わりません。一括攻撃は同じように機能します。2つ目のルール:ソルトは公開されていても構いません。ソルトだけを知っていても、攻撃者は基となるハッシュ関数に対して何の近道も得られません。そのため、ソルトはハッシュの隣のデータベースに保存され、その配置は適切であり、むしろ推奨されます。3つ目のルール:ソルト値は、暗号学的に安全な擬似乱数生成器から生成されます。Linuxでは`getrandom(2)`で公開されています。Nodeでは`crypto.randomBytes`と呼ばれています。Pythonの標準ライブラリでは`secrets.token_bytes`でラップされています。非暗号的な`Math.random()`は、実際の監査コードで必要以上に頻繁に登場しますが、不適切です。

2人が「hunter2」をパスワードに選んだ場合、保存されるハッシュ値は完全に異なるはずです。これを実現するのがソルトです。ソルトを省略すると、データベースからハッシュ値だけでなく、誰が誰とどのパスワードを共有しているかというソーシャルグラフまで漏洩してしまいます。

パスワードソルトはどのように機能するのですか?

この仕組みは4つのステップに分かれており、過去10年間のパスワード情報漏洩事件の事例から、ほぼすべての漏洩は、誰かがこれらのステップのいずれかを省略したことが原因で発生していることが分かっている。

ステップ1は登録時に実行されます。アプリケーションはCSPRNGに16~32バイトのランダムな値を要求します。これがソルトです。OWASPの2025年版ガイドラインでは16バイト(128ビット)を最小値としていますが、auth0や複数のパスワードマネージャーベンダーは32バイトを推奨しています。NISTのSP 800-63B-4では4バイトを最小値としていますが、そのサイズでは約6万5千人のユーザーで誕生日による衝突が発生し始めると警告しており、そのため実際に4バイトを使用する人はいません。

ステップ2では、ソルトとユーザーが選択したパスワードを組み合わせます。最も一般的な形式は連結で、ソルトはパスワード文字列の前または後に配置されます。一部のシステムでは、代わりにHMACを使用し、ソルトをHMACキーとして扱うことで、単純な連結で発生する長さ拡張に関するいくつかの複雑な問題を回避しています。

ステップ 3 では、結合された入力をパスワード ハッシュ アルゴリズム (暗号学の教科書では鍵導出関数、または KDF と呼ばれる) に通します。これは、多くのチームが以前はつまずいていたステップで、汎用ハッシュ アルゴリズムに頼って作業が完了したと考えてしまうことが多かったのです。単純な SHA-256 は、パスワードを保存する方法としては不適切です。2026 年の消費者向け GPU は、1 秒あたり 1,000 億回を超える SHA-256 ハッシュを処理するため、ソルトを使用しても、暗号ハッシュの推測ごとのコストはごくわずかです。ソルトはレインボー テーブル攻撃ベクトルを無効にします。ソルトだけでは、総当たり攻撃によるパスワード クラッキングを遅くすることはできません。ここで適切なツールは、意図的に遅く、メモリ負荷の高い関数です。Argon2id は OWASP が推奨するデフォルトであり、scrypt と bcrypt は許容され、FIPS 140 準拠で選択を強いられる場合は、非常に高い反復回数の PBKDF2 が許可されます。

ステップ4では、ハッシュとソルトの両方を保存します。最新のライブラリは保存形式を処理するため、保存されたソルトを手動で管理する必要はありません。bcryptの出力は`$2b$12$9f4c8a7b...kQR8YZpL9`のようになります。この単一の文字列には、アルゴリズム識別子、コスト係数、ソルト、およびハッシュ値が含まれています。Argon2idは、同様のPHC文字列形式`$argon2id$v=19$m=19456,t=2,p=1$$`を使用します。この文字列をデータベースの列に書き込むだけで、あとは何もする必要はありません。ライブラリは、パスワードを安全に保存するために必要なすべての処理(ソルト用のランダムデータの生成、低速ハッシュアルゴリズムによるパスワードの処理、結果の取得のためのバンドル)を実行済みです。

ログイン処理は、同じパイプラインを逆方向にたどります。アプリケーションはユーザーのソルトと保存されているハッシュを検索し、送信されたパスワードを同じソルトを使用して同じアルゴリズムで処理し、その結果を保存されているハッシュと比較します。比較は定数時間で行われます。定数時間は重要です。単純な `==` では一致したバイト数が漏洩し、タイミング攻撃の脆弱性が生じる可能性があります。`hmac.compare_digest`、`crypto.timingSafeEqual`、または使用しているフレームワークの同等の機能を使用してください。

セキュリティにおける塩漬け

塩を加えることが重要な理由:レインボーテーブルの問題

レインボーテーブルは、ソルト処理が考案された特定の攻撃を阻止するためのものです。あらゆる可能性のあるパスワード(辞書に載っている単語、一般的なバリエーション、漏洩したリストのエントリなど)をSHA-256ハッシュにマッピングする、事前に計算されたルックアップテーブルを想像してみてください。このようなテーブルは存在し、クラッキングフォーラムで販売されており、テラバイト規模になります。攻撃者がソルト処理されていないハッシュのデータベースダンプを入手すれば、レインボーテーブルでハッシュを検索するのはデータベースクエリであり、クラッキング作業ではありません。2012年のLinkedInの攻撃はまさにこの方法で行われました。1億1700万個のソルト処理されていないSHA-1ハッシュがレインボーテーブルに対して攻撃され、約3日間で90%が解読されました。

ユーザーごとにソルトを追加すると、レインボーテーブルは機能しなくなります。攻撃者は、固有のソルト値ごとに個別の事前計算済みテーブルを用意する必要があります。16バイトのランダムソルトの場合、1億1700万人のユーザーに対して1億1700万個の異なるテーブルが必要となり、各テーブルのサイズは依然としてテラバイト単位になります。ストレージコストだけでも、これは非現実的です。したがって、この攻撃は脅威モデルから除外されます。

それがソルトの役割のすべてです。その範囲は意図的に狭くなっています。ソルトを使用しても、特定のユーザーに対する総当たり攻撃の速度を遅くすることはできません。ソルトを使用しても、以前の情報漏洩によるクレデンシャルスタッフィング攻撃を防ぐことはできません。パスワード自体が「123456」の場合、ソルトを使用しても効果はありません。攻撃者は辞書にある明らかな文字列を試しますが、ソルトは推測ごとに再計算されるものの、推測ごとのコストは実質的に変わりません。

ソルト処理が確実に軽減できるもの:レインボーテーブル、および同じデータベース内のユーザー間でのパスワード再利用情報の漏洩。スローハッシュが軽減できるもの:推測ごとのコスト。ペッパーが軽減できるもの:データベースのみの盗難。MFAが軽減できるもの:クレデンシャルスタッフィング。ソルト処理はセキュリティスタックの1つのレイヤーです。パスワードの解読を難しくするためにソルトを追加することは、個々のパスワードを個別に推測しにくくすることとは異なります。ソルト処理が達成する目標は、ユーザーごとに異なるハッシュを作成することです。推測のコストを高くすることは、別の問題です。

ハッシュ化、暗号化、ソルト処理の比較

特に専門家以外の人が書いた文章では、この3つの用語が混同されがちです。これらは互換性のある用語ではありません。

暗号化ハッシュ化塩漬け
可逆はい、鍵でいいえ、意図的に修飾子であり、単体では使用できない
出力長さ変数、入力と一致する固定(通常256ビット)該当なし — ハッシュ入力を変更します
用途データの機密性完全性、パスワードの保存ハッシュを強化する
キーが必要ですはい、秘密ですいいえいいえ、ランダムな塩を使用します

暗号化は、後で復元したいデータを保護します。受信者は鍵を保持し、それを使って元のデータを読み取ります。ハッシュ化は一方向です。パスワードがハッシュ化されると、どのアルゴリズムでもそれを元に戻すことはできません。ソルトはハッシュ関数の入力に適用する修飾子であり、それ自体には意味がありません。

2013年のAdobeの情報漏洩事件は、ここで教訓となる事例です。Adobeは、1億5300万件のユーザー記録を、単一のサイト共通キーの下、ECBモードの3DESでパスワードを暗号化して保存していました。この選択は三重の失敗でした。暗号化はパスワードの保存には不適切な基本手段です。ECBモードでは、同一の入力が同一の出力として漏洩します。データベース全体で1つのキーを再利用したため、キーを一度復号すれば1億5300万件すべての記録が復号されてしまいます。シュナイアー氏はこれを史上最悪のパスワード漏洩事件の1つと呼びました。あらゆるレベルでの対策は、ソルト付きの低速ハッシュに切り替えることでした。

塩対コショウ:二重防御

Pepperは、Saltのあまり知られていない親戚のようなものです。その概念は、アプリケーションが処理するすべてのパスワードに適用される追加の秘密値であり、データベースの外に保存されます。環境変数、キー管理サービスのエントリ、またはHSMに常駐するキーなどです。Saltはハッシュの横に平文で保存されますが、Pepperはそうではありません。

脅威モデルは、データベースのみの窃盗です。攻撃者がSQLインジェクション、設定ミスのあるバックアップ、または漏洩したダンプなどを通じてデータベースだけを盗んだ場合、ソルトとハッシュは入手できますが、ペッパーは入手できません。ペッパーがなければ、攻撃者はブルートフォース攻撃を開始することすらできません。なぜなら、ハッシュ化するすべての推測にグローバルシークレット修飾子が欠けているからです。

一般的な実装では、`argon2id(salt + password + pepper)` を実行するか、より慎重には、まず `HMAC(pepper, password + salt)` を計算し、その結果を Argon2id に渡します。OWASP は、高価値システムには pepper を推奨していますが、トレードオフがあることも認めています。それは、pepper のローテーションが運用上非常に面倒であるということです。ローテーションを行うということは、次回のログイン時にすべてのユーザーのパスワードを再ハッシュすることを意味し、バックアップなしで pepper が失われた場合、すべてのアカウントがチェックできなくなります。ほとんどの消費者向けアプリケーションでは pepper は使用されません。銀行、政府機関、医療機関のワークロードでは通常、pepper は使用されません。

2026年の最新パスワードハッシュ

OWASPの2025年版パスワード保存ガイドラインでは、4つのアルゴリズムが推奨順にランク付けされています。ソルトはこれらのアルゴリズムすべてに組み込まれており、手動で生成する必要はありません。

アルゴリズムOWASP 2025の最低限の要件仕様
Argon2id(推奨) m=19 MiB、t=2、p=1 RFC 9106 (2021)
scrypt N=2^17、r=8、p=1 RFC 7914 (2016)
bcrypt(レガシー)コスト ≥ 10、入力上限 72 バイトニールス・プロヴォス、1999年
PBKDF2-HMAC-SHA256 60万回の反復RFC 2898; FIPSのみ

Argon2id はメモリ負荷が高く、各推測には CPU サイクルだけでなく、定義された RAM のチャンクも必要になります。OWASP の最小要件であるハッシュあたり 19 MiB は、カスタム ASIC を構築する攻撃者が高速メモリも大量に構築する必要があることを意味し、メモリはどのリグでも最も高価な部分です。「id」バリアントは、Argon2i (サイドチャネル耐性) と Argon2d (GPU 耐性) を組み合わせ、前半で Argon2i を、後半で Argon2d を実行します。

scrypt は Argon2id より古く、同じメモリハードの原理で動作します。bcrypt はさらに古く、よりシンプルで、72 バイトの入力制限があり、長いパスフレーズを使用するアプリケーションで時折問題が発生することがあります。より長い入力が必要な場合は、SHA-256 で事前にハッシュ化して base64 エンコードしてください。PBKDF2 は低速ですが FIPS に準拠した選択肢です。OWASP は 2023 年に SHA-256 の反復回数を 310,000 回から 600,000 回に増やしました。

このリストに含まれないもの:通常のSHA-256、通常のSHA-512、MD5、SHA-1、および「+ salt」で終わるカスタム関数。速度が除外基準です。SHA-256は汎用ハードウェア上で高速に動作するように設計されています。これはパスワードストレージに必要なものとは正反対です。

実際の監査でよく見られる、よくある塩漬けミス

実際の情報漏洩報告では、同じような少数のエラーが繰り返し指摘されている。

ユーザーごとに同じソルトを使い回すと、メカニズム全体が無効になり、レインボーテーブルの問題が、たった1つの手順を追加するだけで再び発生します。ユーザー名をソルトとして使用すると、さらに状況が悪化します。ユーザー名はシステム間で重複し、人気のあるシステムではレインボーテーブルを事前に計算できるからです。16バイト未満のソルト長では、大規模なユーザーベース規模で衝突が発生し始めます。暗号化に関係のない乱数関数(`Math.random()`、`time(0)` mod something、言語のデフォルトの乱数生成器など)でソルトを生成すると、予測可能な値が生成され、ソルトの利点が失われます。

より巧妙な間違いは、「セキュリティのため」にソルトを別のサーバーに保存することです。ソルトは秘密情報ではありません。複数のシステムに分散させると、暗号学的強度を高めることなく、運用上の複雑さが増すだけです。ハッシュと同じ行に保存してください。最新のPHC文字列は、これを自動的に行ってくれます。

2026年の監査で最も大きな問題は、SHA-256にソルトを追加してハッシュ化し、それを送信することです。ソルトはレインボーテーブルを防ぎますが、単一のアカウントに対して毎秒100億回の推測を実行するGPUファームには何の対策にもなりません。解決策は2つ目のソルトを追加することではなく、Argon2idのような低速なKDFに切り替えることです。

最後に、従来のソルトなしハッシュについてです。移行は必須です。標準的な手順は、次回のログイン時に従来のハッシュを検証し、最新のアルゴリズムでハッシュを再生成して上書きすることです。その後ログインしないユーザーについては、一定の期間内にパスワードのリセットを強制します。

セキュリティにおける塩漬け

塩漬けだけでは不十分な理由

ソルト処理は防御層の一つに過ぎず、要塞ではありません。ソルト処理は、事前に計算されたテーブルを用いた特定の攻撃を防ぐ効果しかなく、基となるパスワードの強度については何も明らかにしません。ブルートフォース攻撃は依然として脆弱なパスワードに対して有効です。クレデンシャルスタッフィングは、使い回しのパスワードに対して有効です。フィッシングは、あらゆるパスワードに対して有効です。

2026年の真のパスワードセキュリティは、4つの防御策を積み重ねています。16バイトのユーザーごとのソルトを使用したArgon2idがストレージを管理します。ペッパー(オプションですが、機密性の高いシステムでは有効)は、データベースのみの盗難を阻止します。多要素認証は、クレデンシャルスタッフィングとほとんどのフィッシングを阻止します。Have I Been PwnedとそのAPIのような継続的な侵害監視サービスは、ユーザーの認証情報が漏洩した瞬間を検知し、ユーザーに強制的にリセットさせます。

これらの防御層のいずれか一つでも欠けていれば、攻撃者は最終的にその隙間を見つけ出すだろう。ソルト処理だけ、あるいは防御策の一つだけを単独で行うことが、過去10年間のあらゆる侵害事後分析で失敗の原因として指摘されてきたのだ。

質問は?

いいえ。ユーザー名は複数のサービスで重複し、攻撃者はよく使われるサービスのレインボーテーブルを事前に計算し、予測可能なソルトは、ソルト処理によって解消されるはずのレインボーテーブルの問題に逆戻りします。常にCSPRNGからバイトを取得してください。Pythonでは`secrets.token_bytes(16)`、Nodeでは`crypto.randomBytes(16)`を使用します。

OWASPの2025年版ガイドラインでは、ソルトサイズの下限を16バイト(128ビット)としている。NISTは技術的には4バイトのソルトも認めているが、そのサイズでは約6万5千人のユーザーが誕生日関連の衝突に遭遇する。16バイトのソルトであれば、衝突確率は2^64分の1程度になる。実際には、衝突はほぼ発生しない。

はい、レインボーテーブル検索や、複数のユーザー間でのパスワードの使い回しによる情報漏洩に対しては有効です。しかし、攻撃者が特定のアカウントに対して根気強く総当たり攻撃を仕掛けてくる場合には有効ではありません。真の保護には、ソルト処理と、Argon2idのような低速でメモリ負荷の高いハッシュ関数を組み合わせ、さらに理想的には多要素認証(MFA)を重ねる必要があります。

ソルトとは、ハッシュ化の直前にパスワードに混ぜられるランダムな値のことで、共有パスワードであっても保存されたハッシュが毎回一意になる仕組みです。秘密でも鍵でも暗号化でもありません。ハッシュと共に公開され、新しいアカウントごとに再生成されます。

これはパスフレーズに関するヒントであり、ソルト処理のルールではありません。記憶に残りやすく、エントロピーを高めるために、関連性のない3つの単語(「trumpet-glacier-velvet」)を組み合わせることを推奨しています。ソルト処理とは、データベースがユーザーが選択したパスワードをどのように保存するかに関するものです。3単語ルールは、そもそもパスワードを選択する際の考え方に関するものです。どちらの要素も役立ちます。

ソルト処理とは、パスワードがハッシュ化される前に、固有のランダムな値をパスワードに挿入する処理です。つまり、全く同じパスワードを使用しても、2人のユーザーがそれぞれ異なるハッシュ値を取得できるということです。ソルトはアカウントごとに生成され、公開されており、ハッシュ値のすぐ隣のデータベースに保存されます。ソルト処理の真の役割は、レインボーテーブル攻撃を阻止することです。

Ready to Get Started?

Create an account and start accepting payments – no contracts or KYC required. Or, contact us to design a custom package for your business.

Make first step

Always know what you pay

Integrated per-transaction pricing with no hidden fees

Start your integration

Set up Plisio swiftly in just 10 minutes.