ハッシュ関数とパスワードクラック
この連載で毎回使ってきたSHA-256フラグ判定の「正体」に迫ります。ハッシュ関数の不可逆性、サイトがパスワードをハッシュ化して保存する理由、そして辞書攻撃でハッシュから元のパスワードを当てるテクニックを体験します。
📋 目次
🔏 ハッシュ関数とは何か|「不可逆」な指紋を作る
この連載の正解判定を支えてきた仕組みの正体
実はあなたはもう11回もハッシュ関数を使っている
このシリーズのCTFウィジェットは毎回、あなたが入力した答えをcrypto.subtle.digest('SHA-256', ...)でハッシュ化し、正解のハッシュ値と比較していました。フラグの平文をページ内に直接書かないための工夫です。今回はこの仕組みそのものを主役にして掘り下げます。
ハッシュ関数とは、どんな長さの入力データからも、常に固定長の出力(ハッシュ値・ダイジェスト)を生成する関数です。SHA-256なら、1文字の入力でも1GBのファイルでも、必ず256ビット(16進数64文字)の出力になります。重要な性質が3つあります。
- 一方向性(不可逆性):ハッシュ値から元の入力を計算で逆算することはできない
- 決定性:同じ入力なら必ず同じハッシュ値になる(再現性がある)
- アバランチ効果:入力をたった1文字変えるだけで、出力が全く別の値になる
🔒 なぜサイトはパスワードをハッシュ化して保存するのか
「不可逆」だからこそ安全に保存できる
もしサイトがあなたのパスワードを平文(そのままの文字列)でデータベースに保存していたら、データ漏洩が起きた瞬間に全利用者のパスワードがそのまま流出します。一方、ハッシュ値だけを保存していれば、漏洩してもハッシュ値から元のパスワードを直接逆算することはできません。ログイン時は「入力された値をハッシュ化し、保存されたハッシュ値と一致するか」を比較するだけで、サーバーは平文パスワードを一切保持しません。
| 保存方式 | データ漏洩時の被害 |
|---|---|
| 平文保存 | 全利用者のパスワードが即座にそのまま流出。最悪の事態 |
| ハッシュ化(Saltなし) | 逆算は困難だが、辞書攻撃やレインボーテーブルで一部は割れる |
| ハッシュ化(Salt + 低速アルゴリズム) | 1件ずつ個別に総当たりが必要になり、現実的な時間で割れない |
このCTFシリーズでの使い方は「学習用の軽い抑止」
このウィジェットがSHA-256を使っているのは、ページのHTML内にフラグの平文を直接書かないための軽い工夫であり、本物のパスワード保護とは目的が違います。本気で守るべきデータには、次のセクションで説明する専用の手法が必要です。
🗂️ パスワードクラックの手法(辞書攻撃・総当たり攻撃)
「逆算できない」なら「片っ端から試す」
ハッシュ値から直接逆算できないなら、攻撃者は候補となるパスワードを片っ端からハッシュ化し、保存されているハッシュ値と一致するか比較するという方法を取ります。第11話で学んだ「総当たり攻撃」のパスワード版です。
辞書攻撃 vs 総当たり攻撃
辞書攻撃は「よく使われるパスワード集(rockyou.txtなど数百万件規模のリストが実在)」を使う効率重視の方法。総当たり攻撃はa,b,c…のように全パターンを生成して試す網羅的な方法です。短く単純なパスワードほど、どちらの方法でも一瞬で見つかってしまいます。
🌈 レインボーテーブルとSalt(ソルト)による対策
「あらかじめ全部計算しておく」攻撃と、その対策
レインボーテーブルとは、よく使われる文字列とそのハッシュ値の対応表を事前に大量計算しておいた巨大なデータベースです。攻撃者はハッシュ値が手に入った瞬間、このテーブルを検索するだけで一瞬でパスワードを特定できます。これに対抗するのがSalt(ソルト)です。
Saltの仕組み:ランダムな「追加調味料」
パスワードをハッシュ化する前に、利用者ごとに異なるランダムな文字列(Salt)を付け加えます。hash(password + salt)とすることで、同じパスワード「123456」を使っている2人のユーザーでも、保存されるハッシュ値はまったく別物になります。事前に計算されたレインボーテーブルは、ランダムなSaltまでは予測できないため通用しなくなります。
| 用途 | 推奨アルゴリズム | 理由 |
|---|---|---|
| ファイルの改ざん検出 | SHA-256 / SHA-3 | 高速に計算できることが望ましい(整合性チェック用途) |
| パスワード保存 | bcrypt / Argon2 / PBKDF2 | Saltを内蔵し、意図的に低速化&計算コストを高くして総当たりを困難にする |
| デジタル署名 | SHA-256 + 鍵 | 高速なハッシュに鍵情報を組み合わせて署名を実現 |
「SHA-256でパスワードをハッシュ化してます」は実は不十分
SHA-256は高速に計算できることが売りのアルゴリズムです。整合性チェックには最適ですが、パスワード保護には逆効果になります。高速だからこそ攻撃者は1秒間に数十億回もの試行ができてしまうからです。bcryptやArgon2は意図的に「遅い」設計にすることで、この物量攻撃を無力化します([[feedback_registration_form_security]]のチェックリストにもある通りです)。
🧩 5分CTFチャレンジ:ハッシュから元のパスワードを当てろ!
下のVaultに保存されたハッシュを辞書攻撃で解け
下の「SecureVault」には、ある単語のSHA-256ハッシュ値だけが保存されています。候補ワードリストの中から、このハッシュ値と一致する単語を見つけて、Vaultに入力してください。
チャレンジの手順
① F12 → Consoleタブを開く → ② 下のコードをコピーしてConsoleに貼り付けて実行 → ③ CRACKED:に続いて表示される単語を確認 → ④ Vaultのパスワード欄に入力して「解除する」を押す → ⑤ 表示されたフラグを入力フォームへ!
Consoleにそのまま貼り付けられる辞書攻撃コード
(async function(){
var list=['password','123456','qwerty','dragon','master','letmein','sunshine','superman','trustno1','batman','football','hunter2','shadow','michael','jennifer'];
var target='f52fbd32b2b3b86ff88ef6c490628285f482af15ddcb29541f94bcf526a3f6c7';
for(const w of list){
const buf=await crypto.subtle.digest('SHA-256',new TextEncoder().encode(w));
const hex=Array.from(new Uint8Array(buf)).map(b=>b.toString(16).padStart(2,'0')).join('');
if(hex===target){console.log('CRACKED:',w);break;}
}
})();
STORED HASH (SHA-256):
f52fbd32b2b3b86ff88ef6c490628285f482af15ddcb29541f94bcf526a3f6c7
候補ワードリスト:
🔓 Vault解除に成功しました!
辞書攻撃でパスワードを特定しましたね。お見事です。
フラグ:
SecureVaultの解除に成功すると表示されるフラグを入力してください。
上の「Consoleにそのまま貼り付けられる辞書攻撃コード」をコピーし、F12のConsoleタブに貼り付けてEnterキーを押してください。少し待つとCRACKED: ○○○という結果が表示されます。
候補ワードリストの中でこの単語は、かつてIRCチャットで「画面には伏字(★★★★★★★)に見えるのに実は平文で送信されていた」という伝説的なミスで有名になった単語です。気になったら調べてみてください。
📝 まとめ+FAQ+次回予告
今回のポイントを振り返ろう
第12話では、この連載で毎回使ってきたSHA-256ハッシュ関数の正体と、サイトがパスワードをハッシュ化して保存する理由、そして辞書攻撃でハッシュから元のパスワードを当てる手法を学びました。「不可逆だから安全」という思い込みの落とし穴と、Saltや低速アルゴリズムが必要な理由も理解できたはずです。
・ハッシュ関数は不可逆・決定的・アバランチ効果を持つ「指紋」生成器
・サイトはパスワードを平文ではなくハッシュ値で保存するべき
・辞書攻撃・総当たり攻撃でハッシュ値から元のパスワードを特定できる
・レインボーテーブルは事前計算によりこの攻撃をさらに高速化する
・Salt+低速アルゴリズム(bcrypt/Argon2/PBKDF2)が現実的な対策
Q. SHA-256は遅すぎてパスワードに向かないと聞きました。本当ですか?
実は逆です。SHA-256は速すぎるためパスワード保護には向きません。高速に計算できることは整合性チェックには利点ですが、パスワードの場合は攻撃者が1秒間に何十億回も試行できてしまう欠点になります。bcryptやArgon2は意図的に計算を遅くする設計で、この問題を解決しています。
Q. この連載でもSHA-256を使っているのは矛盾していませんか?
このシリーズのSHA-256は「ページのHTMLに正解の平文をそのまま書かない」ための軽い工夫であり、本物のパスワード保護とは脅威モデルが違います([[wp_plugin_dev_notes]]参照)。学習用クイズの答え合わせと、実際の攻撃者に晒される認証システムでは、守るべき強度が全く異なるのです。
Q. Saltをつければ完全に安全になりますか?
Saltはレインボーテーブル攻撃と「同じパスワードのユーザーが同じハッシュ値になる」問題を防ぎますが、個別のパスワードに対する総当たり攻撃の速度そのものは遅くなりません。本当に強くするには、Saltに加えて意図的に低速なアルゴリズム(bcrypt/Argon2/PBKDF2)を使う必要があります。
Q. 自分のパスワードがクラックされやすいか確認する方法はありますか?
「Have I Been Pwned」のようなサービスで、過去の漏洩データベースに自分のパスワードが含まれていないか確認できます。対策としては、辞書攻撃で一発で当てられるような単純な単語を避け、長くランダムなパスフレーズを使い、パスワードマネージャーで管理することが推奨されます。
OSINT入門|公開情報から手がかりを探す
暗号編はここまで。次回からは「OSINT編」がスタート。検索テクニックやメタデータから、公開されている情報を手がかりに調査するCTFの基礎を体験します。
📚 参考情報
- IPA「パスワードのハッシュ化に関する留意事項」(情報処理推進機構)
- OWASP「Password Storage Cheat Sheet」
- NIST SP 800-63B — Digital Identity Guidelines(認証ガイドライン)


コメント