HMAC とハッシュの違い:メッセージ認証になぜ鍵が要るのか

約5分

HMAC(Hash-based Message Authentication Code)は「ハッシュに鍵を加えたもの」とよく説明されますが、なぜそれで認証になるのかは別の問題です。本記事では HMAC とハッシュの違い、そして HMAC が必要な場面を整理します。

ハッシュだけでは認証にならない

SHA-256 のようなハッシュ関数は「メッセージから固定長の指紋を作る」関数です。同じメッセージは同じハッシュを生成するため、改ざん検出に使えます:

sha256("hello") = 2cf24dba...
sha256("hellp") = 4e07408...

しかし、ハッシュだけでは「誰が作ったメッセージか」を保証できません。攻撃者は新しいメッセージを作り、そのハッシュを計算して付け加えることで「正しいメッセージとハッシュ」のペアを作れてしまいます:

偽メッセージ「悪意のあるコマンド」 + そのSHA-256ハッシュ

受信側は「ハッシュが一致しているから正しい」と判断するが、誰でも作れる。

HMAC:鍵で「正しい人だけが作れるハッシュ」にする

HMAC は 送信者と受信者だけが知っている秘密鍵を使ってハッシュを計算します:

HMAC(key, message) = ハッシュに鍵が混ぜ込まれたもの

攻撃者は鍵を知らないため、正しい HMAC を作れません。受信側は自分の鍵で再計算して一致するか確認することで、「鍵を持っている人が作ったメッセージである」ことを検証できます。

HMAC の構造(簡略版)

HMAC-SHA256 の実装は次のようなステップ:

HMAC(K, m) = H( (K ⊕ opad) || H( (K ⊕ ipad) || m ) )
  • H はハッシュ関数(SHA-256 など)
  • K は鍵
  • opad = 0x5c のバイト列、ipad = 0x36 のバイト列
  • || は連結

「鍵を 2 段階で混ぜてからハッシュ」という構造で、単純な H(K || m) には存在する 長さ拡張攻撃を防ぐ設計です。

なぜ単純な H(key || message) ではダメか

SHA-256 は 長さ拡張攻撃に脆弱です。H(K || m) の値が分かっていれば、K を知らなくても H(K || m || padding || extra) を計算できてしまいます。これにより既知のメッセージ+未知の追加メッセージへの認証が偽造可能になります。

HMAC は ipad/opad の二段ハッシュでこれを構造的に防いでいます。

利用シーン

1. JWT の HS256 署名

JWT で alg: HS256 を選ぶと、HMAC-SHA256 でクレームに署名されます。サーバーが秘密鍵を持っていて、トークンが改ざんされていないか検証できる仕組み。

2. Webhook の署名検証

GitHub、Stripe、Slack などの Webhook は、リクエストボディの HMAC をヘッダで送信。受信側で再計算して一致するか確認することで、「本物の通知」と確認できる。

X-Hub-Signature-256: sha256=<HMAC>

3. API リクエストの認証

AWS Signature v4 などは HMAC を使ってリクエストに署名。アクセスキー・シークレットキーのペアでサーバーが検証。

4. セッショントークンの整合性

セッション ID をクライアントが改ざんできないように HMAC を付与する手法。

鍵の管理ポイント

HMAC の安全性は鍵の管理に依存します:

  • 鍵長は 256 bit 以上:HMAC-SHA256 なら最低でも 32 バイトのランダム値
  • 鍵は秘密に保つ:環境変数や Secret Manager で管理、コードに直書きしない
  • 鍵のローテーション:定期的に交換、複数の鍵を並行サポートして移行可能に
  • 比較は constant-time:受信した HMAC との比較は crypto.timingSafeEqual 等を使う(タイミング攻撃を防ぐ)

公開鍵署名(RSA、ECDSA)との違い

HMAC は対称鍵(送受信者が同じ鍵を共有)。公開鍵署名は非対称鍵(秘密鍵で署名、公開鍵で検証)。

  • HMAC:高速、鍵の共有が課題
  • 公開鍵署名:遅い、鍵配布が容易、否認防止性あり

API の Webhook など「サーバー間で鍵を事前共有できる」ケースは HMAC が向く。エンドユーザー向けトークンのように「広く配布する公開鍵で検証したい」場合は公開鍵署名(RS256/ES256)。

まとめ

  • ハッシュだけでは「誰が作ったか」を保証できない
  • HMAC は鍵を混ぜることで認証性を加える
  • 単純な H(key || message) は長さ拡張攻撃に脆弱、HMAC の構造で防ぐ
  • 用途:JWT、Webhook、API 認証、セッション

HMAC を計算してみたいときは、本サイトの HMAC ジェネレーターで鍵とメッセージを入れて SHA-256/SHA-1/MD5 を試せます。