パスワードの設問は、ほぼ「どう保管していたか」と「攻撃者はどこから殴っているか」の2点で決まります。保管方式は平文 → ハッシュのみ → ソルトあり → ストレッチングと段階的に強くなり、段階ごとに防げる攻撃が入れ替わります。そして同じ「パスワードを当てる」でも、認証画面に打ち込むオンライン攻撃と、盗んだ保管データを手元で総当たりするオフライン攻撃では、効く対策がまったく違います。ここを分けて読めるかが分かれ目です。
パスワードはそのまま保存しないのが原則です。代わりにハッシュ値(元に戻せない要約値)を保存し、ログイン時は入力値から計算したハッシュと突き合わせる。この「元に戻せない」性質のおかげで、保管データが漏れても即座に全員のパスワードが分かるわけではありません。ハッシュ関数そのものの性質(一方向性・衝突)は別ページの担当で、ここでは保管方式の選び方と、攻撃側の手口だけを扱います。
保管方式の4段階と、防げるようになるもの
| 保管方式 | この段階で防げるようになること | まだ残る弱点 |
|---|---|---|
| 平文保管 | 何も防げない。運用者も中身を読める状態。 | 漏えい=全利用者が即侵害。他サービスへの使い回しも一斉に破られる。 |
| ハッシュのみ | 保管データを見ただけでは読めない。運用者による直接の閲覧も防げる。 | レインボーテーブル(事前計算表)で一発照合。同じパスワードの利用者が同じ値になり、まとめて割れる。 |
| ソルトあり | 事前計算表を無効化。利用者ごとに値が変わるので1人ずつ計算し直すしかなくなる。 | 1件あたりの計算が速いままなので、短い・単純なパスワードは総当たりで落ちる。 |
| ストレッチング | 1回の試行にかかる時間を意図的に引き上げる。総当たり・辞書攻撃の所要時間が反復回数に比例して伸びる。 | 利用者がありがちな語を使っていれば試行回数自体が少なくて済む。使い回しにも無力。 |
ソルトは何を無効化しているのか
レインボーテーブルは「よくあるパスワード → そのハッシュ値」をあらかじめ計算して並べた表です。表さえ作っておけば、漏れたハッシュ値を引くだけで元のパスワードが分かる。ここで効いているのは「同じ入力なら誰でも同じ値になる」という性質です。
そこで登場するのがソルト=利用者ごとに異なるランダムな追加文字列。パスワードにソルトを足してからハッシュ化すると、同じパスワードでも利用者ごとに別の値になります。すると攻撃者の表は使えません。無効化されているのは「事前に計算しておく」という段取りそのもので、ソルト1件ごとに表を作り直す羽目になります。
ソルトは秘密ではありません。保管データと一緒に置かれ、漏れれば攻撃者にも見えます。それでも成立するのは、狙いが「隠すこと」ではなく「使い回しの計算をさせないこと」だからです。逆に言えば、ソルトが全員同じ値だと目的を果たしません(1つの表を作り直せば全員に使える)。「利用者ごとに違うこと」がソルトの本体です。
オンライン攻撃とオフライン攻撃
ここが午後で最も狙われる分かれ目です。攻撃者がどこで試行しているかで、効く対策が入れ替わります。
オンライン攻撃
- 認証画面・認証APIに実際にログインを試す
- 試行はシステムを通る=記録が残り、回数を数えられる
- 速度はシステムの応答に縛られる(1件ずつ)
- 効く対策:アカウントロック、試行間隔の遅延、多要素認証、送信元単位の制限
オフライン攻撃
- 盗み出した保管データを攻撃者の手元で照合する
- 試行はシステムを一切通らない=ロックもログも効かない
- 専用機材で並列に大量試行できる
- 効く対策:ソルト+ストレッチング、そもそも漏らさない、長いパスワード
アカウントロックが効くのはオンライン攻撃だけです。オフライン攻撃は攻撃者のマシンの中で完結するので、何回失敗しようとロックされる相手がいません。だから設問で「保管データが持ち出された後の対策」を問われたら、ロックや試行回数制限を書いても点になりません。答えるべきはソルトとストレッチング(1件あたりの計算を重くする)、そしてパスワードの再設定です。逆に「ログイン画面への大量試行」ならロック・遅延・多要素認証の側。「どこで試行しているか」を本文から特定してから対策を選ぶのが確実です。
当て方の3種類を区別する
辞書攻撃
- ありがちな語の一覧を順に試す
- 候補数が少なく速い
- 単純な語を使う利用者に刺さる
総当たり攻撃
- 文字の組合せを全部試す
- 必ず当たるが桁が伸びると非現実的
- 長さと文字種で難易度が決まる
パスワードリスト攻撃
- 他所で漏れたID・パスワードの組をそのまま試す
- 使い回しの悪用=当たれば1回で通る
- 強いパスワードでも使い回せば無意味
パスワードリスト攻撃は「弱いパスワード」の問題ではありません。他サービスで正解だった組をそのまま打ち込むので、複雑さを増やしても使い回している限り防げない。しかも1アカウントあたりの試行は数回で済むため、アカウントロックの閾値にも引っかかりにくいのが厄介です。対策は複雑さの強制ではなく使い回しをさせないことと多要素認証。設問で「文字種を増やす規則を設けたのに被害が出た」とあれば、この攻撃を疑います。
試行回数の桁と、反復回数の効き方
ここは計算で聞かれます。数え方を1度整理しておくと迷いません。
候補の総数=(使える文字の種類)の(長さ)乗です。数字だけ(10種類)と、英小文字+数字(36種類)と、英大小+数字+記号(およそ95種類)を、長さ8で比べます。
| 文字種 | 長さ8の候補数 | おおよその桁 |
|---|---|---|
| 数字のみ(10) | 108 | 約1億(8桁) |
| 英小文字+数字(36) | 368 | 約2.8兆(12桁台) |
| 英大小+数字+記号(約95) | 958 | 約6.6×1015(16桁台) |
読み取ってほしいのは、文字種を10→95に増やすと候補は約7万倍、それに対して長さを1文字増やすだけで文字種の倍数だけ増える(95種なら95倍)という関係です。長さは指数側、文字種は底側。同じ手間なら長さを伸ばすほうが桁への効きが大きいため、規則を作るときは複雑さより長さを優先します。
数え方の落とし穴が2つあります。1つは「長さ8ちょうど」と「8文字以下」の取り違え。「以下」なら1文字から8文字までの合計(101+…+108)ですが、最上位の項が支配的なので実質は最長の場合とほぼ同じ。細かく足しても桁は変わりません。もう1つは平均で当たるまでの回数で、全部試すまでではなく候補数のおよそ半分で当たると見積もるのが普通です。「最大何回」と「平均何回」のどちらを問われたかを必ず確認してください。
次にストレッチングの反復回数。攻撃側の所要時間は候補数 × 1回あたりの計算時間で決まります。ハッシュ計算をn回繰り返すと1回あたりの時間がおよそn倍になり、解読時間もそのままn倍になります。1万回なら1万倍、10万回なら10万倍。1日で終わっていた総当たりが、1万倍なら約27年です。
反復回数は正規の利用者側の待ち時間も同じだけ増やします。ログイン1回に数十ミリ秒〜数百ミリ秒かかっても人間には気になりませんが、攻撃者にとってはその数十ミリ秒が候補数のぶんだけ積み上がる。「守る側は1回、攻める側は何億回」という非対称性を利用しているのがストレッチングです。反復回数は機材の性能向上に合わせて見直して引き上げるものだ、という点も押さえておきます。
漏えい後にやること
起きること
- 保管データが手元に渡り、時間無制限でオフライン試行が始まる
- 弱いパスワードから順に落ちていく(全件が同時に割れるわけではない)
- 割れた組は他サービスへのパスワードリスト攻撃に転用される
取るべき手
- 全利用者のパスワードを無効化し再設定させる(ロック強化では止まらない)
- 再設定時に他サービスでの使い回しをやめるよう案内する
- 多要素認証を入れ、パスワード単独で通らない状態にする
- 保管方式をソルト+ストレッチングへ移行する
判断軸は最後まで1つ、「攻撃者はシステムの内側で試しているのか、外側で試しているのか」。内側ならロックと多要素認証、外側なら保管方式と再設定。ここを取り違えなければ、対策の記述で外しません。