MAC と HMAC の設計原理(メッセージ認証はなぜ難しいか)
鍵とハッシュを連結しただけの自作 MAC はなぜ破られるのか。偽造不可能性の定義から HMAC の ipad/opad、Poly1305 の発想までを原理で押さえれば、認証の事故を確実に避けられる。
- 1.MAC は鍵を知る者だけが正しいタグを作れる仕組みで、安全性の基準は「既存タグをいくつ見ても新しいメッセージのタグを偽造できない(EUF-CMA)」こと。秘匿ではなく真正性の道具である。
- 2.HMAC は H(key XOR opad || H(key XOR ipad || msg)) という二重ネスト構造。内側ハッシュで内部状態を隠して長さ拡張攻撃を封じ、安全性は圧縮関数が擬似ランダムであることに帰着して証明される。
- 3.Poly1305 や GHASH は多項式ハッシュ系の一発 MAC。秘密の評価点で多項式を評価し、ハッシュ反復より高速だが nonce/鍵の一意性が崩れると偽造される。HMAC とは設計思想が真逆。
メッセージ認証コードとは何を保証する道具か
メッセージ認証コード(MAC:Message Authentication Code)は、メッセージと秘密鍵から短いタグを計算し、「このメッセージは鍵を知る者が作り、改ざんされていない」ことを受信側が検証できるようにする仕組みです。送信側は tag = MAC(key, msg) を計算してメッセージに添え、受信側は同じ鍵で再計算し一致を確かめます。守るのは真正性・完全性であって機密性ではありません(暗号化との役割分担は ハッシュ化と暗号化の違い と 暗号の基礎 を参照)。
似て非なるものとの線引きが要点です。ハッシュは鍵を持たないので誰でも再計算でき、改ざん者がデータとハッシュを同時に差し替えれば検知できません。デジタル署名は公開鍵で誰でも検証できる「公開検証可能」な道具ですが、MAC は鍵を共有する当事者間でしか検証できない代わりに、署名より桁違いに高速です。MAC は対称鍵の世界の認証プリミティブだと位置づけられます。
偽造不可能性:MAC の安全性はどう定義されるか
MAC の安全性は直感ではなく形式的な攻撃モデルで定義されます。基準は **EUF-CMA(Existential Unforgeability under Chosen-Message Attack)**です。
ゲーム(攻撃者 A 対 検証者):
1. 鍵 key をランダムに選ぶ(A には秘密)
2. A は好きなメッセージ m1, m2, ... を選び、
その正しいタグ MAC(key, mi) を何度でも問い合わせられる(CMA)
3. A は、一度も問い合わせていない新しい m* と、その偽造タグ t* を出力する
4. MAC(key, m*) == t* なら A の勝ち(存在的偽造に成功)
勝率が無視できるほど小さい ⇔ その MAC は EUF-CMA 安全
ここが「メッセージ認証はなぜ難しいか」の核心です。攻撃者は選んだメッセージの正しいタグを好きなだけ集められる前提で、それでもなおたった一つの新しいメッセージのタグすら作れてはいけない。多数のサンプルを与えてもまったく外挿させない、という強い要求がメッセージ認証を難しくしています。なお「メッセージとタグの組」自体が漏れることは想定内で、漏れて困るのは鍵だけです。
理論的には、鍵付き関数が**擬似ランダム関数(PRF)**であれば、その出力をタグとして使うだけで安全な MAC になります。PRF とは「鍵を知らない者には真のランダム関数と区別できない」関数のこと。出力がランダムにしか見えないなら、未知メッセージのタグを当てる確率は当て推量と同じ(出力長 n ビットなら約 2^-n)に抑えられます。HMAC の安全性証明も、本質はこの「HMAC は PRF である」を示すことに帰着します。
素朴な鍵付きハッシュがなぜ壊れるか
「鍵とメッセージを連結してハッシュすればよい」という直感は、SHA-256 のような Merkle–Damgård 構造のハッシュでは破綻します。
危険な自作 MAC:
tag = SHA256(key || msg) # secret-prefix MAC
この構成は**長さ拡張攻撃(length extension attack)**で偽造されます。MD 構造のハッシュは内部状態(chaining 値)をそのまま出力するため、攻撃者は得られた tag を内部状態として復元し、鍵を知らないまま key || msg || padding || 追記 の正しいタグを計算できてしまいます(この機序は ハッシュ関数の内部構造 で詳説)。EUF-CMA の言葉でいえば、msg のタグ一つを観測しただけで新メッセージ msg || ... のタグを偽造できる、明確な敗北です。
では tag = SHA256(msg || key)(secret-suffix)にすれば長さ拡張は防げますが、今度はハッシュ自体に衝突 H(a) = H(b) が見つかると a || key と b || key のタグが一致し、衝突耐性の低下がそのまま MAC の偽造に直結します。鍵とハッシュを単純連結する自作 MAC は、置き方を変えてもどこかで穴が開く——これが HMAC という専用構成が必要になった理由です。
HMAC の構造:ipad と opad の二重ネスト
**HMAC(Hash-based MAC, RFC 2104)**は、任意の Merkle–Damgård ハッシュ H を二重にネストして安全な MAC を作る汎用構成です。
HMAC(K, m) = H( (K' XOR opad) || H( (K' XOR ipad) || m ) )
K' = 鍵をブロック長に整える(長ければ H で縮め、短ければ後ろに 0 を詰める)
ipad = 0x36 をブロック長ぶん繰り返した定数(inner pad)
opad = 0x5c をブロック長ぶん繰り返した定数(outer pad)
動作は二段構えです。まず内側で K' XOR ipad を鍵としたハッシュでメッセージを処理し、固定長の中間ダイジェストを得ます。次に外側で K' XOR opad を頭に置き、その中間ダイジェストを再びハッシュします。
長さ拡張攻撃が成立するのは「内部状態がそのまま外部に晒される」ときでした。HMAC では攻撃者が見るのは外側ハッシュの出力です。外側は内側ダイジェスト(固定長)だけを入力にとり、その内部状態は外に出ません。攻撃者が最終タグを内部状態として復元しても、それは外側ハッシュの状態であり、外側の入力は「内側の出力」という攻撃者が自由に伸ばせない位置にあります。二重ネストが内側の状態を覆い隠す蓋として働くわけです。
ipad と opad が異なる定数(0x36 と 0x5c)なのも理由があります。両者は多くのビットで食い違っており、K' XOR ipad と K' XOR opad は実質的に独立した二つの鍵Ki, Ko のように振る舞います。これにより内側と外側のハッシュが同じ鍵を共有することによる相互作用を避けられます。
HMAC-SHA256、HMAC-SHA1、HMAC-SHA512 はすべて同じ枠組みで、H を差し替えただけです。ハッシュが将来危殆化しても構成を変えずに乗り換えられる(暗号アジリティ)のが設計上の美点。なお SHA-3 は長さ拡張が原理的に成立しないため、KMAC(SHA-3 ベースの鍵付き MAC)のように H(key || msg) 系の素朴な構成でも安全で、HMAC の二重ネストは不要です。HMAC はあくまで MD 構造のハッシュを救うための構成だと理解すると位置づけが明確になります。
HMAC の安全性証明の概略
HMAC が EUF-CMA 安全であることは、ハッシュ全体の理想性ではなく、より弱い前提に帰着して証明されています。骨子は二段階です。
前提: 圧縮関数 f が
(1) 鍵付きで PRF として振る舞う、かつ
(2) (弱い意味の)衝突耐性を持つ
主張: NMAC(HMAC の理論モデル)は PRF である
↓ PRF ⇒ EUF-CMA 安全
結論: HMAC は安全な MAC
理論モデルの NMAC は、内外で独立な二鍵 Ki, Ko を使う構成 f(Ko, f(Ki, m)) です。証明の流れは、(1) 内側 f(Ki, ·) が任意長を固定長に潰す擬似ランダムな関数として働き、(2) 外側 f(Ko, ·) がその固定長出力を擬似ランダムなタグに変換する、という二段で「全体が PRF」を示します。HMAC は NMAC の二鍵を「単一鍵を ipad/opad で派生」して近似したものなので、追加の仮定のもとで NMAC の安全性が HMAC に引き継がれます。
HMAC の安全性は、内側ハッシュの完全な衝突耐性を必要としません。実際、SHA-1 は衝突が現実に作られた後も、HMAC-SHA1 は直ちには破れていません。理由は、HMAC の証明が依存するのは主に圧縮関数の PRF 性であって、攻撃者が見えない秘密鍵込みの衝突を要求するため、鍵なしの公開衝突を作れても HMAC の偽造には届かないからです(とはいえ非推奨。将来の安全余裕を確保するため SHA-256 以降へ移行すべき)。
多項式ハッシュ系 MAC:設計思想の対立軸
HMAC が「汎用ハッシュを反復して安全性を稼ぐ」道具なら、Poly1305 や GHASH に代表される多項式ハッシュ系 MAC は「一回の代数計算で済ませて速度を稼ぐ」道具で、思想が真逆です。これらは情報理論的な **Universal Hashing(普遍ハッシュ)**を土台にします。
Poly1305 の骨格(記号化):
メッセージを 16 バイト断片 c1, c2, ..., cn に分割し係数とみなす
tag = ( c1*r^n + c2*r^(n-1) + ... + cn*r ) mod (2^130 - 5) + s (mod 2^128)
r = 秘密の評価点、s = メッセージごとに変わる一回限りのマスク
メッセージを多項式の係数列、秘密鍵 r をその評価点とみなし、素数 2^130 - 5 を法として一発で評価します。安全性の根拠は EUF-CMA でも PRF でもなく、差分確率の上限です。二つの異なるメッセージが同じハッシュ値を与えるのは「ある多項式が評価点 r で 0 になる」場合に限られ、次数 n の多項式の根は高々 n 個なので、ランダムな r でそれに当たる確率は n / 2^130 程度と極小に抑えられます。
普遍ハッシュ自体は鍵 r を固定すると EUF-CMA 安全ではありません。安全な MAC にするには、評価結果をメッセージごとに変わる一回限りのマスク s で覆う(Wegman–Carter 構成)必要があります。ここで s を再利用、すなわち AEAD で nonce を再利用すると、二つのタグの差からマスクが消去され、評価点 r を未知数とする方程式が解けて r が露出します。r が割れれば任意メッセージのタグを偽造でき、認証は完全に崩壊します(AEAD の設計原理 の nonce 再利用の節と同じ機序)。HMAC が鍵さえ秘密なら同一鍵で何度でも使えるのと対照的です。
| 観点 | HMAC(ハッシュ反復系) | Poly1305/GHASH(多項式系) |
|---|---|---|
| 土台 | ハッシュ圧縮関数の PRF 性 | 情報理論的な普遍ハッシュ + 一回限りマスク |
| 安全性の根拠 | 計算量的(PRF と仮定) | 差分確率の上限(無条件に近い) |
| 鍵/nonce の再利用 | 同一鍵で何度でも安全 | マスク(nonce)再利用で鍵が露出し致命的 |
| 速度 | ハッシュ2回ぶん。十分速い | 1パスの代数演算で非常に高速 |
| 主な用途 | JWT, API署名, TLS の PRF, HKDF | AEAD の認証部(GCM, ChaCha20-Poly1305) |
実装上の落とし穴:タグ比較は定数時間で
理論的に安全な MAC でも、検証側のタグ比較を素朴に書くと破られます。if (tag == expected) のような先頭から比較して不一致で早期 return する実装は、一致したバイト数で処理時間が変わり、攻撃者はタグを 1 バイトずつ計測で当てられます(サイドチャネル攻撃 のタイミング攻撃)。
タグの照合は必ず定数時間比較で行います。全バイトを XOR して累積し、最後にゼロかどうかだけを見る実装にすれば、どこで食い違っても処理時間は一定です。標準ライブラリの hmac.compare_digest(Python)や crypto.timingSafeEqual(Node.js)はこのために用意されています。アルゴリズムの偽造不可能性と実装のサイドチャネルは別問題で、両方塞いで初めて安全になります。
まとめ
メッセージ認証が難しいのは、攻撃者が選んだメッセージのタグを好きなだけ集めても新しいメッセージのタグ一つ作れない、という EUF-CMA の強い要求を満たさねばならないからです。鍵とハッシュの単純連結は長さ拡張や衝突でこの要求を破り、だからこそ HMAC の ipad/opad 二重ネストが必要になります。HMAC は外側ハッシュで内部状態を隠して長さ拡張を封じ、安全性は圧縮関数の PRF 性に帰着して証明されます。一方 Poly1305 などの多項式系は普遍ハッシュと一回限りマスクで高速かつ証明可能な認証を実現しますが、その代償として nonce・マスクの一意性が生命線になります。汎用性と再利用耐性なら HMAC、速度と AEAD 統合なら多項式系——用途で道具を選ぶ判断は、暗号の基礎 の「目的で使い分ける」原則の延長線上にあります。
セキュリティ Article
MAC と HMAC の設計原理(メッセージ認証はなぜ難しいか)を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
MAC
比較で見る軸
難易度: advanced / カテゴリ: セキュリティ / タグ数: 5
導入後に効く点
HMAC は H(key XOR opad || H(key XOR ipad || msg)) という二重ネスト構造。内側ハッシュで内部状態を隠して長さ拡張攻撃を封じ、安全性は圧縮関数が擬似ランダムであることに帰着して証明される。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- セキュリティ
- タグ数
- 5
判断チェックリスト
- 自社の用途が「MAC / HMAC」に近いか確認する。
- 強みである「MAC は鍵を知る者だけが正しいタグを作れる仕組みで、安全性の基準は「既存タグをいくつ見ても新しいメッセージのタグを偽造できない(EUF-CMA)」こと。秘匿ではなく真正性の道具である。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。