HMACとは - 定義と概要
HMAC(Hash-based Message Authentication Code、ハッシュベースメッセージ認証コード)は、暗号学的ハッシュ関数と秘密鍵を組み合わせてメッセージの完全性と認証を同時に提供する技術です。1996年にMihir BellareとRan Canuettiらによって設計され、1997年にRFC 2104として標準化されました。現在はRFC 2104を更新したRFC 6234が最新の標準仕様となっています。
HMACの最大の特徴は、単純なハッシュ関数では実現できない「送信者の認証」機能を提供する点です。通常のハッシュ値は誰でも計算できますが、HMACは秘密鍵を持つ者だけが正しい認証コードを生成できるため、メッセージの送信者が正当であることを確認できます。また、メッセージが通信途中で改ざんされていないことも同時に保証します。
HMACは、SHA-256、SHA-512、SHA-3など、任意の暗号学的ハッシュ関数と組み合わせて使用できる柔軟な設計となっています。最も一般的に使用されるのはHMAC-SHA256で、TLS 1.2/1.3、IPsec、JWT(JSON Web Token)のHS256署名アルゴリズム、AWS Signature V4、GitHub Webhooks、Stripe APIなど、現代のインターネットインフラストラクチャの根幹を支える重要な技術として広く採用されています。
セキュリティ面では、HMACはハッシュ関数の衝突耐性にのみ依存し、一方向性や第二原像攻撃耐性には依存しない設計となっています。これにより、ハッシュ関数に一部の脆弱性が発見されても、HMAC自体のセキュリティが維持される可能性があります。実際、SHA-1には衝突攻撃が発見されていますが、HMAC-SHA1は現在でも特定の用途では安全と考えられています(ただし新規実装では避けるべきです)。
技術的な仕組みと計算アルゴリズム
HMACの計算アルゴリズムは、2回のハッシュ関数適用を組み合わせた構造になっています。基本的な計算式は以下の通りです:
HMAC(K, m) = H((K' ⊕ opad) || H((K' ⊕ ipad) || m))
K: 秘密鍵
m: メッセージ
H: ハッシュ関数(SHA-256など)
K': 鍵をハッシュ関数のブロックサイズに調整したもの
ipad: 内側パディング定数(0x36の繰り返し)
opad: 外側パディング定数(0x5Cの繰り返し)
⊕: XOR演算
||: 連結演算
計算手順の詳細
- 鍵の準備:秘密鍵Kがハッシュ関数のブロックサイズより長い場合はハッシュ化し、短い場合はゼロパディングしてK'を作成します(SHA-256の場合は64バイト)。
- 内側のハッシュ計算:K'とipad(0x36を64回繰り返したもの)をXORし、その結果にメッセージmを連結してハッシュ化します。
- 外側のハッシュ計算:K'とopad(0x5Cを64回繰り返したもの)をXORし、その結果に内側のハッシュ値を連結して再度ハッシュ化します。
- 最終出力:外側のハッシュ計算の結果がHMAC値となります。
なぜ2回ハッシュするのか
この2段階構造には重要な理由があります。単純に「H(K || m)」とすると長さ拡張攻撃に脆弱になります。長さ拡張攻撃とは、H(m)を知っている攻撃者が秘密鍵を知らなくてもH(m || m')を計算できてしまう攻撃です。HMACの2段階構造はこの攻撃を防ぎ、さらにハッシュ関数の内部状態が外部に漏れないように保護します。
歴史的背景と標準化
1990年代中盤、インターネットの商用利用が拡大する中で、安全なメッセージ認証の必要性が急速に高まりました。当時使用されていた単純なハッシュベースの認証方式には様々な脆弱性が発見されており、理論的に安全性が証明できる方式が求められていました。
1996年、IBMの研究者Mihir BellareとPhilip Rogaway、そしてRan Canuettiらが、既存のハッシュ関数を安全に利用できるHMAC構造を提案しました。この設計は、ハッシュ関数を「ブラックボックス」として扱い、その暗号学的特性(特に衝突耐性)のみに依存する形で安全性を証明できる点が画期的でした。
1997年2月にRFC 2104として標準化されて以降、HMACは急速に普及し、SSL/TLS、IPsec、IKE、SSH、さらには後年のHTTP署名、OAuth、JWTなど、現代のセキュリティプロトコルの基盤技術となっています。
AI時代における活用
AI APIの認証とレート制限
OpenAI、Anthropic、Google AI、AWSなどのAI APIでは、HMACベースの署名認証が標準的に使用されています。特にAWS Bedrock APIではSignature V4と呼ばれるHMAC-SHA256ベースの認証方式を採用しており、APIリクエストのURL、ヘッダー、ボディ全体をHMACで署名することで、リクエストの完全性と送信者の認証を保証しています。これにより、APIキーの盗聴や中間者攻撃のリスクを大幅に低減できます。また、HMACタイムスタンプを組み込むことで、リプレイ攻撃(過去の正当なリクエストを再送する攻撃)も防止できます。
AIモデルのWebhook検証
AI推論サービスやMLOpsパイプラインでは、モデルの学習完了、推論結果の生成、異常検知などのイベントをWebhookで通知する仕組みが一般的です。例えばAWS SageMakerやGoogle Vertex AIは、トレーニングジョブの完了時にWebhookを送信しますが、このペイロードにHMAC署名を付与することで、通知が正当なソースから送信されたものであることを受信側で検証できます。これは特に、複数のAIサービスを統合するMLOpsパイプラインにおいて、誤った指示や悪意のあるデータ注入を防ぐために重要です。
RAGシステムにおけるドキュメント整合性検証
RAG(Retrieval-Augmented Generation)システムでは、外部ドキュメントデータベースから取得したテキストをLLMのコンテキストに注入しますが、この過程でドキュメントが改ざんされていないことを保証する必要があります。ベクトルデータベース(Pinecone、Weaviate、Qdrantなど)にドキュメントを格納する際、原文のHMAC値も同時に保存しておくことで、検索時にドキュメントの整合性を検証できます。これにより、プロンプトインジェクション攻撃やデータポイズニング攻撃のリスクを軽減できます。100万件以上のドキュメントを扱う大規模RAGシステムでも、HMAC検証のオーバーヘッドは無視できるレベルです。
AI生成コンテンツの来歴証明
AIが生成したテキスト、画像、動画の出所を証明するために、HMACを使った電子署名が活用されています。例えば、企業の公式AIアシスタントが生成した回答に対してHMAC署名を付与することで、後から「このコンテンツは確かに我々のAIシステムが生成したものである」ことを証明できます。Adobe Content Credentials(C2PA標準)やOpenAIのContent Credentialsもこの原理を応用しており、生成AI時代のコンテンツ信頼性を担保する基盤技術となっています。
エージェントシステム間の安全な通信
複数のAIエージェントが協調動作するマルチエージェントシステム(AutoGPT、LangChain Agents、CrewAIなど)では、エージェント間のメッセージ交換の安全性が課題となります。各エージェントが共有秘密鍵を持ち、送信メッセージにHMAC署名を付与することで、「なりすましエージェント」の混入や、悪意のある指示の注入を防ぐことができます。特に、外部ツールを呼び出すエージェント(ブラウザ操作、データベースアクセス、決済処理など)では、HMAC認証が必須のセキュリティ要件となっています。
実装例とベストプラクティス
Python実装
import hmac
import hashlib
import time
# HMAC-SHA256の生成
def create_signature(secret_key: str, message: str) -> str:
"""メッセージのHMAC-SHA256署名を生成"""
return hmac.new(
secret_key.encode('utf-8'),
message.encode('utf-8'),
hashlib.sha256
).hexdigest()
# タイミング攻撃に安全な署名検証
def verify_signature(secret_key: str, message: str, signature: str) -> bool:
"""署名を定数時間で検証(タイミング攻撃対策)"""
expected_signature = create_signature(secret_key, message)
# 通常の == 比較はタイミング攻撃に脆弱!
return hmac.compare_digest(signature, expected_signature)
# Webhook検証の実例
def verify_webhook(payload: str, received_signature: str, secret: str) -> bool:
"""Webhookペイロードの署名を検証"""
calculated_signature = create_signature(secret, payload)
return hmac.compare_digest(
f"sha256={calculated_signature}",
received_signature
)
# API認証ヘッダーの生成(AWS Signature風)
def create_api_auth_header(api_key: str, timestamp: int, request_body: str) -> dict:
"""APIリクエスト用の認証ヘッダーを生成"""
message = f"{timestamp}:{request_body}"
signature = create_signature(api_key, message)
return {
"X-Timestamp": str(timestamp),
"X-Signature": signature
}
Node.js実装
const crypto = require('crypto');
// HMAC-SHA256署名の生成
function createSignature(secretKey, message) {
return crypto
.createHmac('sha256', secretKey)
.update(message)
.digest('hex');
}
// Express.jsミドルウェアでの署名検証
function verifyWebhookSignature(req, res, next) {
const signature = req.headers['x-signature'];
const secret = process.env.WEBHOOK_SECRET;
const payload = JSON.stringify(req.body);
const expectedSignature = createSignature(secret, payload);
if (crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
)) {
next();
} else {
res.status(401).json({ error: 'Invalid signature' });
}
}
HMACとその他の認証技術の比較
| 技術 | 特徴 | 主な用途 |
|---|---|---|
| HMAC | 共通鍵暗号、高速、鍵配送が課題 | API認証、TLS、JWT HS256 |
| デジタル署名 | 公開鍵暗号、否認防止、計算コスト高 | 証明書、JWT RS256、ブロックチェーン |
| AEAD暗号 | 暗号化と認証を統合、機密性も提供 | TLS 1.3、VPN、ディスク暗号化 |
セキュリティ上の注意点
1. ハッシュアルゴリズムの選択
- 推奨: HMAC-SHA256、HMAC-SHA512、HMAC-SHA3
- 非推奨: HMAC-MD5(MD5の脆弱性)、HMAC-SHA1(新規実装では避ける)
2. 鍵管理
- 秘密鍵は環境変数や専用の鍵管理サービス(AWS KMS、HashiCorp Vaultなど)で管理
- ソースコードに直接埋め込まない
- 定期的な鍵のローテーション(90日〜180日ごと)
- 鍵の長さは最低でもハッシュ関数の出力長と同じ(SHA-256なら32バイト以上)
3. タイミング攻撃対策
- 署名検証には必ず定数時間比較関数を使用(Python:
hmac.compare_digest()、Node.js:crypto.timingSafeEqual()) - 通常の文字列比較演算子(==、===)は使用しない
4. リプレイ攻撃対策
- タイムスタンプやnonceをメッセージに含める
- サーバー側で受信時刻との差分を検証(通常は5分以内)
- 処理済みnonceをキャッシュして二重処理を防止
主要APIサービスでのHMAC活用事例
AWS Signature Version 4
AWS APIへのすべてのリクエストは、HMAC-SHA256ベースの署名を使用します。リクエストのHTTPメソッド、URI、クエリ文字列、ヘッダー、ボディをCanonical Request形式に変換し、そこにタイムスタンプとアクセスキーIDを含めて署名を生成します。
GitHub Webhooks
GitHubは、Webhookペイロードに対してHMAC-SHA256署名をX-Hub-Signature-256ヘッダーで提供します。受信側は設定した秘密トークンを使って署名を検証し、リクエストが本当にGitHubから送信されたものかを確認できます。
Stripe API
Stripe WebhooksはHMAC-SHA256署名を使用し、さらにタイムスタンプを含めることでリプレイ攻撃を防止しています。Stripe-Signatureヘッダーには、タイムスタンプと署名が両方含まれており、受信側は5分以内のリクエストのみを受け入れます。
関連用語
- デジタル署名 - 公開鍵暗号を使った署名技術、否認防止機能を提供
- 公開鍵暗号 - HMACとは異なり鍵配送問題を解決する暗号方式
- HTTPS - TLS通信でHMACがMAC機能として使用される
- TLS証明書 - HTTPS通信の基盤となる証明書の仕組み
- 鍵管理 - HMAC秘密鍵の安全な管理手法
- bcrypt - パスワードハッシュ化に特化したアルゴリズム
- Argon2 - 最新のパスワードハッシュアルゴリズム標準
- Let's Encrypt - 無料SSL/TLS証明書の発行サービス
参考リソース
- RFC 2104: HMAC: Keyed-Hashing for Message Authentication - HMACの原典となる標準仕様書
- NIST FIPS 198-1: The Keyed-Hash Message Authentication Code (HMAC) - 米国標準技術研究所によるHMAC標準
- HMAC - Wikipedia - HMACの歴史と技術的背景の詳細解説
よくある質問(FAQ)
HMACと単純なハッシュ関数の違いは何ですか?
単純なハッシュ関数は誰でも計算できますが、HMACは秘密鍵を持つ者だけが正しい認証コードを生成できます。また、HMACは長さ拡張攻撃に対する耐性があり、メッセージの完全性だけでなく送信者の認証も保証します。これにより、中間者攻撃やメッセージの改ざんを防ぐことができます。
HMACで最も推奨されるハッシュアルゴリズムは何ですか?
現在最も推奨されるのはHMAC-SHA256です。SHA-1は衝突攻撃に対する脆弱性が発見されているため、新規実装では避けるべきです。さらに高いセキュリティが必要な場合はHMAC-SHA512やHMAC-SHA3を検討してください。AWS、GitHub、Stripeなど主要なAPIプロバイダーもHMAC-SHA256を標準として採用しています。
HMACの署名検証でcompare_digestを使う理由は?
通常の文字列比較(==演算子)はタイミング攻撃に脆弱です。比較処理が不一致を見つけた時点で即座に終了するため、処理時間の違いから秘密情報を推測される可能性があります。hmac.compare_digest()は定数時間比較を実行し、どの位置で不一致が発生しても同じ時間で処理を完了するため、タイミング攻撃を防ぐことができます。
JWTのHS256とHMACの関係は?
JWTの署名アルゴリズムHS256は、HMAC-SHA256のことです。JWTではヘッダーとペイロードをBase64URLエンコードして結合したものに対してHMAC-SHA256で署名を生成します。この署名により、トークンが改ざんされていないこと、そして正しい秘密鍵を持つ発行者から発行されたことを検証できます。
HMACの秘密鍵の推奨される長さは?
RFC 2104によれば、鍵の長さはハッシュ関数の出力長と同じかそれ以上が推奨されます。HMAC-SHA256の場合は32バイト(256ビット)以上、HMAC-SHA512の場合は64バイト(512ビット)以上が望ましいです。鍵が短すぎると総当たり攻撃のリスクが高まり、長すぎても追加のセキュリティ効果は限定的です。
