HMAC(Hash-based Message Authentication Code)は、平文ハッシュ関数では解決できない問題を解決します。メッセージが信頼できる送信者から届いたことと、改ざんされていないことを同時に証明できるのです。Webhook の署名をデバッグしたり、API リクエスト署名を実装したことがあれば、HMAC を使ったことがあるはずです。このガイドでは仕組み・使いどころ・コードなしで HMAC 値を生成する方法を解説します。

HMAC とは

HMAC は暗号ハッシュ関数と秘密鍵を組み合わせてメッセージ認証コードを生成する構造です。メッセージ M と鍵 K が与えられると:

HMAC(K, M) = H((K ⊕ opad) || H((K ⊕ ipad) || M))

H はハッシュ関数(SHA-256・SHA-512 など)、opadipad は固定パディング定数、|| は連結です。

端的に言えば:HMAC はハッシュを秘密鍵に結びつけます。鍵を知らなければ同じ出力を再現できません。これが平文ハッシュとの根本的な違いです。

平文ハッシュHMAC
秘密鍵が必要NoYes
改ざんを検出YesYes
送信者の同一性を証明NoYes
誰でも偽造可能YesNo

主なユースケース

Webhook 署名の検証

GitHub・Stripe・Twilio などのサービスは HMAC-SHA256 を使って Webhook ペイロードに署名します。Webhook を受信したら、共有秘密鍵でリクエストボディの HMAC を計算し、ヘッダーの署名と比較します。

X-Hub-Signature-256: sha256=3d23ab...

署名が一致すれば、ペイロードは正当かつ無改ざんです。

API リクエスト署名(AWS Signature V4)

AWS は HMAC-SHA256 を使って API リクエストに署名します。署名はリージョン・サービス・日付・秘密鍵にリクエストを結びつける複数の HMAC 操作の連鎖です。これによりリプレイ攻撃を防ぎ、認可の偽造を不可能にします。

JWT 署名(HS256)

HS256 アルゴリズムで署名した JSON Web Token は HMAC-SHA256 を使います。サーバーは秘密鍵で header.payload に署名します。クライアントはリクエストに JWT を含め、サーバーは HMAC を再計算して署名が一致しないトークンを拒否します。

Cookie・セッションの整合性

署名付き Cookie は改ざん防止に HMAC を使います。サーバーは HMAC(secret, cookie_value) を Cookie に付加します。以降のリクエストでサーバーは HMAC を再計算して検証してから Cookie の内容を信頼します。

HMAC と平文ハッシュの違い

SHA256("hello") のような平文ハッシュは公開情報です。誰でも計算できます。HMAC は秘密鍵の知識を要求します。これが重要なのは:

  • Webhook 検証:HMAC なしでは、攻撃者は任意の値にハッシュされるペイロードを偽造できます
  • トークン署名:HMAC なしでは、クライアントが JWT ペイロードを変更してハッシュを再計算できます

HMAC が必要な場面で平文 SHA256 を使ってはいけません。オーバーヘッドは無視できる程度で、セキュリティの差は大きいです。

サポートされるアルゴリズム

アルゴリズム出力長備考
HMAC-SHA-256256ビット(64文字の16進)ほとんどの用途のデフォルト
HMAC-SHA-384384ビット(96文字の16進)より高いセキュリティマージン、低速
HMAC-SHA-512512ビット(128文字の16進)64ビットプラットフォームで推奨

新規実装では HMAC-MD5・HMAC-SHA1 を避けてください。HMAC は基礎となるハッシュの衝突脆弱性をある程度軽減しますが、これらは多くのコンプライアンスフレームワークで禁止されているレガシーアルゴリズムです。

出力フォーマット

HMAC 出力は生バイトで、以下のいずれかでエンコードできます。

  • Hex3d23ab4f... — 1バイト2文字、ほとんどの API で標準
  • Base64PSOrT... — よりコンパクト、HTTP ヘッダーや JWT で使用

どちらも同じバイト列をエンコードしています。手動デバッグには Hex(読みやすい)、バイト効率が重要な場面には Base64(HTTP ヘッダー・トークン)を使いましょう。

コードでの HMAC 実装

JavaScript(Web Crypto API)

async function hmacSha256(key, message) {
  const enc = new TextEncoder();
  const cryptoKey = await crypto.subtle.importKey(
    'raw',
    enc.encode(key),
    { name: 'HMAC', hash: 'SHA-256' },
    false,
    ['sign']
  );
  const signature = await crypto.subtle.sign('HMAC', cryptoKey, enc.encode(message));
  return Array.from(new Uint8Array(signature))
    .map(b => b.toString(16).padStart(2, '0'))
    .join('');
}

const sig = await hmacSha256('my-secret-key', 'hello world');
// → "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"

Node.js

const crypto = require('crypto');

const hmac = crypto.createHmac('sha256', 'my-secret-key')
  .update('hello world')
  .digest('hex');

console.log(hmac);
// b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7

Python

import hmac
import hashlib

key = b'my-secret-key'
message = b'hello world'

signature = hmac.new(key, message, hashlib.sha256).hexdigest()
print(signature)
# b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7

Go

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
)

func main() {
    mac := hmac.New(sha256.New, []byte("my-secret-key"))
    mac.Write([]byte("hello world"))
    signature := hex.EncodeToString(mac.Sum(nil))
    fmt.Println(signature)
}

Webhook の正しい検証方法

定数時間比較を使う

HMAC 署名を比較する際は常に定数時間比較関数を使ってください。素朴な == 比較は最初の不一致バイトで短絡し、タイミング攻撃に悪用できるタイミング情報を漏らします。

// NG — タイミング情報が漏れる
if (receivedSig === expectedSig) { ... }

// OK — 定数時間比較
const crypto = require('crypto');
if (crypto.timingSafeEqual(Buffer.from(receivedSig), Buffer.from(expectedSig))) { ... }

タイムスタンプを含める

リプレイ攻撃は有効な署名付きリクエストを再利用します。署名するペイロードにタイムスタンプを含め、数分以上古いリクエストを拒否しましょう。

HMAC-SHA256(secret, timestamp + "." + body)

GitHub の Webhook はこのパターンを5分の許容ウィンドウで使用しています。

オンライン HMAC ジェネレーターを使う

ZeroTool HMAC ジェネレーターを試す →

メッセージと秘密鍵を入力し、アルゴリズム(SHA-256・SHA-384・SHA-512)を選択すると、Hex または Base64 で HMAC を即座に取得できます。活用シーン:

  • 期待値を再現して Webhook 署名の不一致をデバッグ
  • 実装が既知のテストベクターと一致することを確認
  • テストコードを書かずに開発中の API 署名を生成
  • HMAC の動作の学習・探索

すべての計算は Web Crypto API を使用してブラウザ内で実行されます。鍵とメッセージがデバイスの外に出ることはありません。

鍵の長さと鍵管理

  • 最小鍵長:HMAC-SHA256 には少なくとも 32 バイト(256ビット)を使用してください。短い鍵はセキュリティを低下させます。
  • 鍵のローテーション:HMAC 鍵を定期的にローテーションしてください。多くのプラットフォームは短い重複ウィンドウで複数のアクティブ鍵をサポートしています。
  • 鍵をログに記録しない:HMAC 鍵はシークレットです。アプリケーションログやエラーレポートから除外してください。
  • 用途ごとに鍵を分ける:Webhook 署名と JWT 署名に同じ鍵を使わないでください。

HMAC 署名を即座に生成・検証する →