PR 本記事には広告(Amazonアソシエイト・もしもアフィリエイト・A8.net等)が含まれます。掲載情報の正確性には努めていますが、商品の詳細は必ずリンク先で最新情報をご確認ください。
CTF・セキュリティ学習

【ハッシュ・MAC編】衝突・Length Extension・HMAC誤用|CTF思考フレームワーク #56

かも次郎とアンペンが「ハッシュ・MAC」を解説するマスコットイラスト
安全に生きたい編集部

こんにちは、アンペンです!

今回はハッシュ関数とMACの編。衝突攻撃・Length Extension・HMAC誤用といった、『暗号化ではないが、認証・整合性で誤ると致命的』な領域を扱います。

パスワード保存・APIトークン署名・ファイル整合性検証……日常のあちこちで使われる仕組みですが、誤用パターンも豊富です。

まず大前提から。ハッシュは『暗号化』ではありません。暗号化は鍵があれば元に戻せますが、ハッシュは一方通行——指紋から元の人間を復元できないのと同じで、ダイジェストから元データには戻せないんです。だから『パスワードをハッシュ化して保存』が成り立つわけですね(戻せないから、漏れても元が分からない…はずでした)。この“戻せない”という性質が、今日の話の土台になります。

MD5って今でも使われてるの?

セキュリティ用途では絶対NG。MD5は2004年、SHA-1は2017年に実用的な衝突が見つかってる。チェックサム用途以外では使わない。

ハッシュ関数って、ダウンロードしたファイルの『チェックサム』やパスワード保存など、意外と身近なところで働いています。役割はざっくり『データの指紋を取る』こと。でも——この“指紋”、用途を間違えるとあっさり偽造されたり、追記改ざんを許したりと、事故が驚くほど多い領域なんです。今日は、ハッシュ・MAC・パスワード保存という“似ているけど別物”の3つを、きちんと区別できるようにしましょう。

まず結論

ハッシュ・MAC編の3大論点は(1)MD5/SHA-1は衝突発見済みでセキュリティ用途禁止、(2)Merkle-Damgard構造(SHA-2まで)はLength Extension攻撃に脆弱、(3)HMACを使う際は『SHA-256 or SHA-3+定数時間比較』が鉄則。パスワードは別物で、bcrypt/scrypt/Argon2を使います。

この記事で分かること

  • ハッシュ関数の選び方(SHA-256/SHA-3/BLAKE)
  • Length Extension攻撃の原理
  • HMACの正しい使い方と典型ミス
  • パスワード保存はハッシュではなくKDF
難易度:中級向け 所要時間:13分 体験:hashpump+HMAC自作 おすすめ:#55の後

📖 はじめてのWebセキュリティ #56|ハッシュ・MAC編
衝突・Length Extension・HMAC誤用を扱います。 シリーズ一覧を見る →

⚠️ 大事なお約束
他者システムの認証/署名を無断で偽造することは違法です。CTFや自分の検証環境のみで確認してください。

ハッシュ関数の現在地

ハッシュ関数は『任意長の入力 → 固定長の指紋(ダイジェスト)』を返す関数。衝突困難性・原像困難性・第2原像困難性の3つの性質が安全性の柱です。

図解:使ってよい/避けるべきハッシュ

同じ『ハッシュ』でも、衝突発見済みのものと現役のものでは扱いが完全に違います。下の表を頭に入れておくと選定に迷いません。

『MD5やSHA-1はもう使うな』と何度も言われますが、これは“古いから”ではなく“壊れたから”です。本来ハッシュは『違うデータからは違う指紋が出る』のが命。ところがMD5やSHA-1は、『中身が違うのに同じ指紋になるペア』を意図的に作る方法が見つかってしまった。指紋を偽造できる=本人確認の根っこが崩れる、ということです。だから“チェックサム(うっかりミスの検出)程度”ならまだしも、悪意を想定する場面では完全にアウトなんです。

MD5・SHA-1は避けるべき、SHA-256・SHA-3・BLAKEは現役という現代のハッシュ関数選択基準の図解
図1:MD5/SHA-1は退役、SHA-256/SHA-3/BLAKEが現役
🪪 たとえるなら、指紋認証

ハッシュ関数は『データの指紋』を取る作業。違う人(データ)なら違う指紋になる、というのが衝突困難性です。MD5やSHA-1は『偽の指紋』を意図的に作る方法が見つかってしまった状態で、もう本人確認には使えません。SHA-256やSHA-3は現在のところ偽指紋の作り方が見つかっていないので現役、というイメージです。

偽造された指紋でハッシュの本人確認が崩壊する衝突攻撃のたとえイラスト
図2:偽の指紋が作れる=衝突発見=本人確認の崩壊

ここで覚える用語:Length Extension攻撃
SHA-1・SHA-2(SHA-256/512)はMerkle-Damgard構造を使っており、H(secret | msg) の結果と msg の長さが分かれば、secretを知らずに H(secret | msg | padding | extra) を計算できてしまいます。MAC代わりに使うと『追記改ざん』が通る致命傷になります。SHA-3とBLAKE2/3は構造が違うのでこの攻撃は効きません。

MACは『改ざん検知』のためにある

ハッシュとMACの違い

  • ハッシュ:誰でも計算できる(鍵なし)。データの指紋
  • MAC:鍵を知る者だけが計算できる(鍵あり)。改ざん検知
  • HMAC:MACの代表実装。HMAC(K, M) = H((K⊕opad) | H((K⊕ipad) | M))
  • 誤った実装:H(K | M) ←Length Extension脆弱

HMAC誤用3パターン

HMAC自体は安全ですが、使い方で穴を作るのが定番です。

ここで初心者がやりがちな罠が『自前で H(秘密+メッセージ) を作ってMAC代わりにする』こと。一見よさそうですが、SHA-2系には“続きを勝手に書き足せる”Length Extensionという弱点があるため、秘密鍵を知らない攻撃者でも、メッセージの末尾に好きな追記をした“正規のMAC”を作れてしまうんです。だから車輪の再発明はせず、最初からこの罠を塞いである『HMAC』を使う。これが鉄則です。

  • ① 自前で H(K|M)HMACでなくSHA-1を直接連結 → Length Extensionで突破
  • ② 通常の == 比較:タイミング攻撃で1バイトずつ判明 → 必ず定数時間比較(hmac.compare_digest)
  • ③ MACを切り詰める:64bit以下に短縮するとBirthday boundで衝突
  • 避けるべき:MD5-HMAC, SHA-1-HMAC(MAC用途なら一応動くが、置き換え推奨)
  • 採用するなら:HMAC-SHA-256 / HMAC-SHA-3 / KMAC
自前H(K|M)・==比較・切り詰めという3つのHMAC誤用パターンを横並びカードで示した図解
図3:HMAC誤用3パターン(自前/タイミング/切り詰め)

パスワード保存は『ハッシュ』ではなく『KDF』

『パスワードをSHA-256でハッシュして保存する』はもうダメです。SHA-256は高速すぎるため、GPUで秒間数十億回試行できてしまいます。

ここ、直感に反するポイントです。普通『速い』のは長所ですが、パスワード保存に限っては『速い=弱点』。なぜなら攻撃者は、盗んだハッシュに対して、辞書の単語を片っ端からハッシュ化して突き合わせる総当たりをするから。SHA-256はGPUで秒間数十億回も計算できてしまうので、速ければ速いほど、攻撃者の総当たりも爆速になる。守る側にとっては、むしろ“遅い”ほうがありがたいんです。

  • bcrypt:古典の定番。コスト調整可、最大72バイト制限
  • scrypt:メモリも食わせて並列攻撃を妨害
  • Argon2(id):現代の最推奨。OWASP公式推奨
  • 共通点:意図的に遅い+メモリ消費+ソルト

そこで登場するのがKDF(鍵導出関数)です。bcryptやArgon2は、わざと『遅く・メモリを食う』ように設計されています。正規ログインは1回0.1秒程度なら誰も困りませんが、攻撃者が何十億回も繰り返そうとすると、その“わざとの重さ”が積もって現実的に不可能になる。さらに各ユーザーに別々の『ソルト』を混ぜるので、まとめて総当たりする近道も塞げます。『速さを捨てて安全を買う』——これがパスワード保存の発想なんです。

CTFでやってみよう:Length Extensionを自作で試す

やってみよう / 自分の環境・CTFのみ

『H(secret|msg)』を H(…|extra) に拡張する

目的は『ハッシュをMAC代わりに使うとなぜ破れるか』を体感することです。

  1. Pythonで SHA-256(secret + b"user=guest") を計算する
  2. secret長は知ってる前提で、hashpumpyhash_extender で extension を作る
  3. 結果が SHA-256(secret + b"user=guest&padding&user=admin") として有効化を確認
  4. 同じ実装をHMAC-SHA-256に置き換えて、攻撃が通らないことを確認
  5. 応用:hmac.compare_digest 使わないコードでタイミング差を測ってみる
本番システムや他者の署名に対して同じ攻撃を試さないこと。検証は隔離環境のみで。

ここまでをいったん整理しましょう。ポイントは『似ているけど、用途ごとに道具がきっぱり違う』こと。指紋を取るだけならハッシュ、改ざんを防ぎたいならMAC、パスワードを預かるならKDF。同じ“ハッシュっぽい何か”でも、目的を取り違えると一気に穴が空きます。

守る側:『ハッシュは指紋、MACは封印、パスワードはKDF』

用途別チートシート
  • データ整合性検証:SHA-256 or SHA-3 or BLAKE3(MD5/SHA-1禁止)
  • 認証/署名:HMAC-SHA-256以上またはEd25519署名
  • パスワード:Argon2id (推奨) / bcrypt / scrypt
  • API Token:JWT(HS256/EdDSA) or PASETO
  • 比較は定数時間関数(hmac.compare_digest, secrets.compare_digest)
  • 独自実装しない、標準ライブラリに任せる

『MD5なら早いから』でパスワード保存してたら一発で抜けるね。

そう。次回はCrypto章の最終回、楕円曲線。ECDH・ECDSA・Smart’s Attackを扱うよ。

ひと言でまとめると、『ハッシュ=指紋、MAC=封印、パスワード=わざと重い金庫』。この3つを混同しないだけで、現場でやりがちな事故の大半は防げます。そして迷ったら——MD5/SHA-1は使わない、MACはHMAC、パスワードはArgon2id。この3つの合言葉を覚えておけば十分です。

まとめ:『ハッシュ・MAC・KDFは別物』

今回のポイント
  • MD5/SHA-1は衝突発見済み。セキュリティ用途禁止
  • SHA-2はLength Extensionに弱い → MACならHMAC一択
  • パスワードはArgon2id等のKDFを使う
  • 比較は定数時間で(タイミング攻撃回避)

今日の持ち帰りは『“とりあえずハッシュ”が一番危ない』。ハッシュ・MAC・KDFは見た目こそ似ていても、果たす役割はまったく別。用途に合った道具を選び、比較は必ず定数時間で、独自実装はしない。この3点を押さえれば、認証まわりの“うっかり事故”はぐっと減らせます。

次回は暗号章の最終回、楕円曲線・近代暗号編。ECDH・ECDSA・Smart’s Attackを扱います。

次に読みたい記事

参考資料

記事URLをコピーしました