はじめに
情報漏えいのニュースで、「エンジニアは何をしていたんだ」という声を目にしました。
正直に言うと、侵入そのものを100%防ぐのは難しいと思います。
脆弱性を突かれることもあれば、委託先やソフトウェアの不具合が原因になることもあります。
ただ、盗まれたあとの被害の大きさは設計次第で変えられる部分があります。
その代表がパスワードのハッシュ化です。
この記事では、ハッシュ化の「アルゴリズム」と「重さ(コストパラメータ)」に絞って、なぜそこが実際の耐性を分けるのかを整理します。
ハッシュ化や暗号化の基本は、前の記事でまとめました。
この記事は、その続きとして読んでもらえるとつながりやすいと思います。
1. ハッシュ化は「盗まれたあと」の対策
先に大事な前提です。
ハッシュ化は、情報が盗まれること自体を防ぐ対策ではありません。
盗まれたあとに、パスワードを読み取られにくくする対策です。
盗まれたDBを手に入れた攻撃者は、自分のマシンでパスワード候補を片っ端から試します。
これがオフライン攻撃です。
オフラインなので、サービス側のログイン回数制限もかかりません。
攻撃者がどれだけ速く試せるかが、そのまま攻撃の成功率に直結します。
この章のまとめ
- ハッシュ化は、盗まれたあとの被害を小さくする対策
- 盗まれたDBに対する攻撃は、ログイン制限が効かない
- 攻撃者の試行速度を遅くできるかどうかがポイント
2. SHA-256やMD5がパスワード保存に向かない理由
SHA-256やMD5は、もともと速く計算できるように作られたハッシュ関数です。
ファイルの整合性確認などでは速いことが長所になります。
でもパスワード保存では、これが弱点になります。
GPU(画像処理用のチップ)は、単純な計算を大量に同時にこなすのが得意です。
速いハッシュ関数とGPUの組み合わせだと、1秒に膨大な数の候補を試せてしまいます。
Fast hashing algorithms such as SHA-256 are not suitable for password storage (SHA-256のような高速なハッシュアルゴリズムは、パスワードの保存には適していません。)
参考:OWASP Password Storage Cheat Sheet
ここで「ソルトを付ければいいのでは」と思うかもしれません。
ソルトは、事前計算した対応表(レインボーテーブル)での逆引きを防ぎます。
でも1回あたりの計算が速いことは変わりません。
攻撃者は盗んだDBからソルトも見られるので、ソルトごと総当たりすればいいだけです。
つまり、ソルトを付けたSHA-256でも、1件あたりのGPUでの総当たりの速さは変わりません。
ただしソルトがあることで、全ユーザー分をまとめて照合することはできなくなり、DB全体を破るコストは件数に比例して増えます。
ソルトについての詳細な説明は、冒頭で紹介した前の記事でしています。
この章のまとめ
- SHA-256やMD5は、速いのが長所で、パスワード保存では弱点になる
- ソルトは逆引き対策で、総当たりの速度は変えない
- 耐性を分けるのは、計算コストを意図的に高くしてあるかどうか
3. 計算を重くしたハッシュ関数
そこで使うのが、わざと重く作ったハッシュ関数です。
- bcrypt:計算の重さを調整できる。1999年に発表された定番
- scrypt:計算に加えて大量のメモリも使わせる設計
- Argon2(Argon2id):メモリも時間も調整できる新しい方式。RFC 9106(Informational)で仕様が公開されている
- PBKDF2:内部でHMAC-SHA-256などを何十万回も繰り返して重くする方式。FIPS-140準拠が必要な場合の選択肢
scryptやArgon2はメモリハードと呼ばれます。
計算のたびに大きなメモリが必要になるので、GPUなどで同時に回せる数が減り、攻撃のコストが上がります。
OWASPの優先順位は、Argon2id、使えなければscrypt、どちらも使えないレガシー環境ではbcryptです。FIPS-140準拠が必要な場合はPBKDF2になります。
参考:OWASP Password Storage Cheat Sheet 、RFC 9106「Argon2」
4. コストパラメータが耐性を決める
ここからが本題です。
bcryptやArgon2を選んでも、設定値が低ければ意味が薄れます。
アルゴリズムの選定と同じくらい、パラメータの設定が大事です。
bcryptのcost factor
bcryptの重さはcost factorという数値で決まります。
1上げるごとに、計算量が約2倍になります。
| cost | cost 4を1とした計算量 |
|---|---|
| 4 | 1倍 |
| 6 | 4倍 |
| 10 | 64倍 |
| 12 | 256倍 |
cost 4〜6あたりの低い値のまま使うと、GPUでの総当たり耐性はかなり落ちます。
ライブラリのサンプルコードに低い値が書いてあることもあるので、そのまま本番に入れてしまわないように注意したいところです。
ライブラリの既定値も更新されます(PHPは8.4.0でbcryptの既定costが10から12に引き上げられました)。
OWASPの最低ラインはcost 10以上です。
またbcryptには、パスワードは72バイトまでしか見ないという制限もあります。
Argon2のパラメータ
Argon2には3つの設定値があります。
- メモリ使用量(m):計算中に使うメモリ(単位はKiB)。大きいほどGPUに不利になる
- 反復回数(t):計算を繰り返す回数
- 並列度(p):メモリを分けて並べる計算の系列(レーン)の数。スレッド数と同じとは限らず、上げれば必ず遅くなるわけでもない
OWASPが示す最低ラインは、メモリ19MiB・反復2回・並列度1です。
サーバーの事情に合わせて、メモリを減らして反復を増やすような組み合わせも載っています。これらは同等の防御力として挙げられている組み合わせです。
| メモリ(m、KiB) | 反復回数(t) | 並列度(p) |
|---|---|---|
| 47104(46MiB) | 1 | 1 |
| 19456(19MiB) | 2 | 1 |
| 12288(12MiB) | 3 | 1 |
| 9216(9MiB) | 4 | 1 |
| 7168(7MiB) | 5 | 1 |
scryptのパラメータ
scryptは、CPU/メモリコスト(N)・ブロックサイズ(r)・並列度(p)で調整します。
OWASPの最低ラインは、N=2の17乗・r=8・p=1(メモリ約128MiB)です。
Nを下げてpを上げる同等の組み合わせ(例:N=2の16乗・r=8・p=2)も載っています。
PBKDF2のパラメータ
FIPS-140準拠が必要な場合は、HMAC-SHA-256を使い、反復回数を600,000回以上にするのがOWASPの記載です。
最低ラインを並べると
| アルゴリズム | 最低ライン |
|---|---|
| Argon2id | メモリ19MiB・反復2回・並列度1 |
| scrypt | N=2の17乗・r=8・p=1 |
| bcrypt | cost 10以上・72バイトまで |
| PBKDF2(FIPS-140準拠が必要な場合) | HMAC-SHA-256・600,000回以上 |
なお、これはOWASPの最低ラインです。RFC 9106自体は、より重い設定を推奨しています(第一推奨:t=1・p=4・メモリ2GiB、メモリが少ない環境向けの第二推奨:t=3・p=4・メモリ64MiB)。
また、アルゴリズムごとに仕組みが違うため、scryptの約128MiBとArgon2idの19MiBのように、数値をそのまま比べることはできません。
参考:OWASP Password Storage Cheat Sheet
これらは最低ラインです(2026年10月16日時点のOWASPの記載)。
更新されることもあるので、実装するときは最新の記載を確認したほうがよさそうです。
この章のまとめ
- アルゴリズムが良くても、パラメータが低ければ耐性は落ちる
- bcryptはcostが1上がるごとに計算量が約2倍
- Argon2はメモリ・反復回数・並列度、scryptはN・r・pで調整する
- OWASPの最低ラインを目安にする
5. コストを上げるデメリット
重くすればするほど安全、という単純な話でもありません。
デメリットも整理します。
- ログインが遅くなる:1回のハッシュ計算が重くなるので、待ち時間も延びる
- サーバー負荷が増える:Argon2やscryptはメモリを大量に使うので、同時ログインが集中するとメモリ不足になりうる
- DoSや負荷増大に悪用されうる:重い処理を大量に呼ばれると、サーバーを疲弊させる攻撃に使われる可能性がある
- 環境に依存する:開発環境の感覚と、本番サーバーの性能がズレやすい
- bcryptは72バイトの制限がある:長いパスフレーズは途中から無視される。OWASPは最大長を72バイト以下に制限することを求めている(日本語はUTF-8で1文字3バイトのことが多く、24文字ほどで超える)
対策の方向性としては、次のようなものが考えられます。
- 本番相当の環境で実測して、ログインの待ち時間が許容できる範囲でいちばん高い値を選ぶ
- ログイン試行の回数制限をかけて、重い処理を乱発されないようにする
- 同時ログインのピーク時にもメモリが足りるか、見積もっておく
「ユーザー体験」と「攻撃者の手間」のバランス取りなので、正解は一つではないと思います。
最低ラインを下回らない範囲で、サービスの特性に合わせて決める形になりそうです。
6. 設定はあとから強化できるようにしておく
ハードウェアは年々速くなります。
今の設定が、数年後も十分とは限りません。
パスワードの元の値は分からないので、保存済みのハッシュから直接、新しい設定で計算し直すことはできません。
そこで、ログイン成功のタイミングで保存し直す方法がよく使われます。
流れはこうです。
- ログインが成功したとき、入力されたパスワードが手元にある
- 保存されているハッシュが古い方式や低いコストなら、新しい設定でハッシュ化し直して保存する
これならユーザーに何もさせずに少しずつ強化できます。
ただし、ログインしないユーザーの古いハッシュは残り続けます。
ハッシュ値に方式やパラメータが埋め込まれる形式のライブラリを使うと、古い設定かどうかも判定しやすいと思います。
SHA-256のような古い保存方式から移行する場合は、旧ハッシュ値をそのまま新しいハッシュ関数の入力にする方法もあります。
ログインを待たずに、全員分を一気に強化できるのが利点です。
ただし、この方法は旧ハッシュの強度に依存します。特に旧保存がソルトなしのSHA-256などである場合、旧ハッシュ値自体への辞書攻撃の影響が残るため、暫定措置と位置づけるべきです。OWASPは、次にログインしたときに元のパスワードから直接ハッシュし直して置き換えることを勧めています。
また、bcryptに渡す場合は、旧ハッシュ値をbase64などの文字列にしてから渡します(バイト列のままだとNULバイトの扱いで問題が起きます)。
7. 最近の漏えい事案から考える
最近の漏えい事案を見てみます。
KDDIのISP向けメールシステム
2026年6月に発覚したKDDIのISP事業者向けメールシステムへの不正アクセスについて、個人情報保護委員会が8月19日に行政上の対応を発表しています。
| 項目 | 人数 |
|---|---|
| ID(メールアドレス)とパスワードの漏えいが確認された人数 | 1,223万1,954人 |
| うち、パスワードが平文で保存されていた人数 | 761万6,173人 |
うち761万6,173人のパスワードは平文のまま保存されており
参考:個人情報保護委員会「KDDI株式会社及びインターネットサービスプロバイダに対する個人情報の保護に関する法律に基づく行政上の対応並びにメールサービス利用者に対する注意喚起について」
参考:ScanNetSecurity「KDDI の ISP 事業者向けメールシステムに不正アクセス、個人情報保護委員会が行政指導」
この事案では、約761万人分のパスワードが平文保存でした。
残りの約462万人分の保存方式は、公表文では示されていません。
今回の記事のアルゴリズムやパラメータ以前の話です。
個人情報保護委員会の公表文によると、多くのパスワードを平文で保存していたシステムだったことを踏まえ、侵入後の横展開を防ぐ多層的な対策が必要だったと判断しています。
また、平文で保存されていたのは一部のプロバイダ側の事情に起因するものとされ、当該プロバイダにも指導が行われています。
他にも続くパスワード漏えい
他にもパスワードを含む漏えいが続いています。
| 事案 | 公表時期 | パスワードに関する公表内容 |
|---|---|---|
| ショップサーブ(運営:Eストアー) | 2026年8月 | 会員IDとパスワードが漏えい対象に含まれる。公式は「暗号化された状態で管理」と記載(方式は未公表) |
| ソースネクスト | 2026年10月15日 | 対象件数は最大2251万9960件。対象情報にパスワードを含む(全員分とは限らない)。保存形式は公式発表では未言及 |
INTERNET Watchは、ソースネクストの件でパスワードのリセット対応などから、平文で流出したとみられると伝えています。
参考:ソースネクスト「当社サイトへの不正アクセスによるお客様情報流出のお詫びとお知らせ」
参考:INTERNET Watch「ソースネクスト、不正アクセスでパスワード含む会員の個人情報最大約2252万件が流出」
これは報道側の見立てで、公式発表が保存方式を明らかにしているわけではありません。
ショップサーブの件は、公式が「暗号化された状態で管理」としていますが、可逆の暗号化かハッシュ化かなど、方式は公表されていません。「暗号化」とだけ書かれていても、鍵も一緒に盗まれたかどうかや、前の記事で整理したハッシュ化との違いは読み取れません。
参考:Eストアー「不正アクセスによる個人情報漏えいに関するお知らせ(第2報)」
なので、保存方式が分からない事案をまとめて平文と決めつけることはできません。
ただ、パスワードを含む情報が漏れた以上、事業者の判断にかかわらず、利用者に使い回しの変更を求める流れになるのは自然だと思います。
適切にハッシュ化されていたらどうなるか
もしこれらが、適切に設定されたArgon2idやbcryptだったら、どうなっていたでしょうか。
攻撃者は盗んだDBから、オフラインで1件ずつ総当たりすることになります。
重い設定なら、1件あたりに時間とコストがかかります。 弱いパスワードは破られるかもしれませんが、全員分を一気に使える状態にはなりにくいはずです。
もちろん、ハッシュ化は万能ではありません。
- 「123456」のような弱いパスワードは、重くしても短時間で当たる
- 他サービスと同じパスワードを使い回していると、被害が広がる
- 保存方式以外の対策(アクセス制御や多要素認証など)も別途必要
それでも、盗まれたあとの被害をどこまで小さくできるかは、保存方式でかなり変わると思います。
エンジニアに情報漏えいは防げないのか
「エンジニアに情報漏えいは防げないのか」という問いには、「侵入を完全に防ぐのは難しい。でも被害を小さくする設計はできる」と答えるのが、現実的なんじゃないでしょうか。
パスワードの保存方式は、利用者には見えない部分です。
だからこそ、作る側が「なんとなくbcrypt」ではなく、どのアルゴリズムを、どのパラメータで使っているかまで把握しておく価値があると思います。
気になるのは、既存システムの設定値が、数年前のまま放置されていないかです。
一度見直してみるきっかけになればと思います。
さいごに
最後に整理します。
- ハッシュ化は、盗まれたあとの被害を小さくする対策
- ソルトは逆引き対策で、総当たりの速度は変えない
- bcrypt・scrypt・Argon2のように計算コストを高くしたものを使っているかが、実際の耐性を分ける
- コストパラメータが適切かどうかも、アルゴリズム選定と同じくらい重要
- 重くすればするほど、ログインの遅延やサーバー負荷といったデメリットもある
- あとから強化できる仕組みを入れておくと、設定の見直しが楽になる
実装するときは、OWASPのチートシートで最新の推奨値を確認してもらえればと思います。
少しでも参考になれば幸いです。
参考リンク
- OWASP Password Storage Cheat Sheet
- RFC 9106:Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications
- 個人情報保護委員会:KDDI株式会社及びインターネットサービスプロバイダに対する個人情報の保護に関する法律に基づく行政上の対応並びにメールサービス利用者に対する注意喚起について
- ScanNetSecurity:KDDI の ISP 事業者向けメールシステムに不正アクセス、個人情報保護委員会が行政指導
- ソースネクスト:当社サイトへの不正アクセスによるお客様情報流出のお詫びとお知らせ
- INTERNET Watch:ソースネクスト、不正アクセスでパスワード含む会員の個人情報最大約2252万件が流出
- Eストアー:不正アクセスによる個人情報漏えいに関するお知らせ(第2報)
- PHP マニュアル:password_hash