仕組みから学ぶ Web
応用読了目安 14#パスワード#ハッシュ#ソルト#Argon2#bcrypt

パスワードの保存 — 漏れたあとに、何日持ちこたえられるか

「パスワードはハッシュ化していたので安全」と発表した数日後、大半が元に戻され、他のサービスで不正ログインが続いた。速いハッシュは、漏れた瞬間から総当たりの的になる。

この記事の進み方

What — 漏れたハッシュは、どう元に戻されるか

パスワードを保存するときにハッシュ化するのは、DB が漏れてもパスワードそのものを渡さないためです。ハッシュ関数は一方向で、値から入力を計算できません。

ところが、計算できないことと、見つけられないことは別です。

Figure流出したハッシュから、パスワードが見つかるまで
  1. 1テーブルを手に入れるハッシュ値とメールアドレス軽い
  2. 2同じハッシュ値をまとめる同じパスワードは同じ値になる重い
  3. 3よく使われるパスワードを試す過去の流出から集めた一覧重い
  4. 4規則的に変形して試す末尾に数字、記号に置き換え重い
  5. 5他のサービスで試す使い回しを狙う重い

どの段も、ハッシュの計算が速いほど早く進む。遅くする仕組みが、そのまま時間稼ぎになる。

1/5
テーブルを手に入れる流出したのは会員テーブル。ハッシュ値の列がそのまま入っている。ここからは攻撃者の手元の計算機で、サービス側は何もできない。

攻撃者は逆算しない。候補をハッシュ化して、一致するものを探す。速いハッシュほど、1秒に試せる候補が多い。

ハッシュ関数
入力から固定長の値を計算する関数。同じ入力は同じ値になり、値から入力は計算できない。
ソルト
利用者ごとに異なるランダムな値。パスワードに足してからハッシュ化し、同じパスワードでも違う値にする。
ストレッチング
ハッシュの計算を意図的に重くすること。1回の照合に時間やメモリを要するようにする。
ペッパー
DB とは別の場所に置く秘密の値。DB だけが漏れても、それが無ければ照合できない。

速さは、利用者ではなく攻撃者の味方

SHA-256 のような汎用のハッシュ関数は、速く計算できるように作られています。ファイルの改ざん検知や署名では、それが長所です。

パスワードの保存では、この速さが逆に働きます。

  • 利用者がログインするとき: 1回計算するだけ。1回が1ミリ秒でも1秒でも、体感はあまり変わらない
  • 攻撃者が総当たりするとき: 数十億回、数兆回と計算する。1回あたりの時間が、そのまま所要時間に掛かる

1回あたりの計算を重くすると、利用者の負担はほとんど変わらず、攻撃者の負担だけが桁違いに増えます。 パスワード用のハッシュ関数(Argon2id、scrypt、bcrypt)は、この重さを意図して設計され、重さを調整できるようになっています。

確認 — ここまで読めたか

パスワードに利用者ごとのランダムなソルトを足してから SHA-256 でハッシュ化し、ソルトは同じ行に保存しています。テーブルが流出したとき、特定の1人のパスワードを総当たりで見つける時間は、ソルトが無い場合と比べてどうなりますか?

まず選ぶ(解答例は a〜d の記号で説明します)

Why — なぜ「ハッシュ化」だけでは足りないのか

パスワードの保存で守りたいのは、流出を防ぐことだけではありません。流出は、脆弱性、内部不正、バックアップの紛失など、いくつもの経路で起きえます。

だから本当に問うべきは、**「漏れたあと、どれだけ時間を稼げるか」**です。

稼いだ時間で、サービス側は流出に気づき、全員にパスワードの変更を求め、使い回しの危険を知らせられます。数日で大半が割れるなら、知らせる前に被害が広がります。 数年かかるなら、ほとんどの会員は間に合います。

Compareパスワードの保存方法

漏れた瞬間に全部読める

password = 'hunter2'
漏れたあと
**即座に全員分**
同じパスワードの人
そのまま見える
管理者から
**読める**
稼げる時間
0
  • DB を見られる人全員が、会員のパスワードを知っている状態
  • 「パスワードを忘れた」ときに元のパスワードを送れるサービスは、これか暗号化
  • 論外だが、ログや問い合わせの記録に平文が残っている、という形で起きがち

下に行くほど、漏れたあとに稼げる時間が長い。「元に戻せないか」ではなく「見つけるのにどれだけかかるか」で比べる。

自分で組み合わせない

パスワード用のハッシュは、実績のあるライブラリの関数を、その推奨設定で使うのが基本です。

  • ソルトの生成、ハッシュ値の文字列への埋め込み、照合時の比較は、ライブラリが面倒を見る
  • 比較を == で自前に書くと、一致した文字数によって処理時間が変わり、手がかりを与えることがある
  • bcrypt は入力の先頭72バイトまでしか使わないなど、関数ごとの制約がある。長い入力を前処理する場合は、その方法もライブラリの推奨に従う

重さの設定は、ログイン1回あたりに許容できる時間(数十〜数百ミリ秒)を目安にし、計算機が速くなるのに合わせて定期的に引き上げます。設定はハッシュ値の文字列に含まれるので、次のログインのときに新しい設定で作り直すことができます。

確認 — ここまで読めたか

パスワードを AES で暗号化して DB に保存しています。鍵はアプリの設定ファイルにあります。DB と一緒に、アプリのサーバーの設定ファイルも流出しました。どうなりますか?

まず選ぶ(解答例は a〜d の記号で説明します)

演習 — まず自分で判断する

解説を読む前に、まず自分で判断してみてください。ここで一度詰まっておくと、 次の節の判断軸が「なるほど」ではなく「そう来たか」に変わります。

演習 — 設計判断を問う

新しく作るサービスで、パスワードをどう保存しますか?

与えられた条件
  • メールアドレスとパスワードでログインする
  • ログインのピークは毎秒50回程度。サーバーは数台
  • 数年運用する予定で、途中で設定を強くしたくなる可能性がある
  • チームにセキュリティの専門家はいない
  • 秘密情報はシークレット管理サービスに置ける
この軸で考える
  • · 流出したあと、1人あたり何回の照合を強いられるか
  • · 何年か後に設定を強くしたいとき、既存の会員をどう移すか
  • · 重い照合が、サーバーの負荷と攻撃の入口にならないか
まず選ぶ(解答例は a〜d の記号で説明します)

演習 — 説明できるか

流出の発表文の案に「パスワードはハッシュ化しているため安全です」と書かれています。広報とマネジメントに、この表現の何が問題かと、何を書くべきかを伝えてください

与えられた条件
  • パスワードは SHA-256 で、ソルト無しでハッシュ化されていた
  • 相手は技術的な詳細より、会員への影響と会社の信頼を気にしている
  • 発表は今日中に出す必要がある

読み終わりましたか?

読了にすると、これを前提とする記事がロードマップで開放されます。