HMAC解説
HMAC(Hash-Based Message Authentication Code)は、暗号学的ハッシュ関数と秘密鍵を組み合わせて、メッセージの整合性と authenticity の両方を検証するメカニズムです。誰でも計算できるプレーンハッシュとは異なり、HMACは認証コードの生成または検証に秘密鍵の知識を必要とします。
SHA256のようなプレーンハッシュは、メッセージが変更されていないことを示しますが、誰が作成したかは証明しません。攻撃者はメッセージを変更した後にハッシュを再計算できるため、受信者は改ざんを検出できません。HMACは秘密鍵をハッシュ計算に組み込むことでこれを解決します。鍵を持つ者のみが有効なHMACを生成できるため、秘密を共有する当事者間でのメッセージ認証に適しています。
HMACの仕組み
HMACは、秘密鍵を使用してハッシュ関数を2回実行します。アルゴリズムはRFC 2104で定義されており、次のように動作します:
- 秘密鍵をハッシュ関数のブロックサイズにパディング
- パディングされた鍵を内部パディング定数(ipad = 0x36を繰り返し)とXOR
- 内部パディング鍵にメッセージを追加
- 結果をハッシュ化
- パディングされた鍵を外部パディング定数(opad = 0x5Cを繰り返し)とXOR
- ステップ4のハッシュを外部パディング鍵に追加
- 結果を再度ハッシュ化
HMAC(K, m) = H((K' ⊕ opad) || H((K' ⊕ ipad) || m))
この二重ハッシュ方式は長さ拡張攻撃を防ぎ、基盤となるハッシュ関数に脆弱性があっても、HMAC構造が安全であることを保証します。
HMAC vs プレーンハッシュ
| 特徴 | プレーンハッシュ(SHA256) | HMAC-SHA256 |
|---|---|---|
| 鍵が必要 | いいえ | はい |
| 整合性チェック | はい | はい |
| 認証 | いいえ | はい |
| 改ざん検出 | 完全な再計算 | 鍵なしでも検出可能 |
| 最適な用途 | ファイルチェックサム | API認証 |
| 長さ拡張 | 脆弱 | 耐性あり |
一般的なユースケース
API認証 — クライアントは共有秘密鍵を使用して各リクエストにHMACで署名します。サーバーはHMACを再計算して比較します。一致すれば、リクエストは本物と見なされます。
JWTトークン署名 — JWTは通常、よりシンプルなHMACまたはRSA署名を使用しますが、HMACはJWTの有効なアルゴリズムです(HS256、HS384、HS512)。
Webhook検証 — Stripe、GitHub、Slackなどのサービスは、WebhookペイロードにHMACで署名するため、サービスからの送信であることを検証できます。
セッショントークン — HMACは、データベースに保存せずに偽造不可能なセッショントークンを生成できます。
コード例
PHP
$secret = 'my-secret-key';
$message = 'message-to-authenticate';
// HMACを生成
$hmac = hash_hmac('sha256', $message, $secret);
echo $hmac;
// HMACを検証
$expected = hash_hmac('sha256', $message, $secret);
if (hash_equals($expected, $providedHmac)) {
echo 'Authentic';
}
Python
import hmac
import hashlib
secret = b'my-secret-key'
message = b'message-to-authenticate'
# HMACを生成
hmac_value = hmac.new(secret, message, hashlib.sha256).hexdigest()
print(hmac_value)
# HMACを検証(定数時間比較)
expected = hmac.new(secret, message, hashlib.sha256).digest()
received = bytes.fromhex(provided_hmac)
if hmac.compare_digest(expected, received):
print('Authentic')
JavaScript(Node.js)
const crypto = require('crypto');
const secret = 'my-secret-key';
const message = 'message-to-authenticate';
// HMACを生成
const hmac = crypto.createHmac('sha256', secret)
.update(message)
.digest('hex');
console.log(hmac);
// 検証
const expected = crypto.createHmac('sha256', secret)
.update(message)
.digest();
HMACアルゴリズム
HMACは任意の暗号学的ハッシュ関数を使用できます。アルゴリズム名は通常、「HMAC」にハッシュ名を組み合わせたものです。
| アルゴリズム | ハッシュ関数 | 出力サイズ |
|---|---|---|
| HMAC-MD5 | MD5 | 128ビット |
| HMAC-SHA1 | SHA-1 | 160ビット |
| HMAC-SHA256 | SHA-256 | 256ビット |
| HMAC-SHA512 | SHA-512 | 512ビット |
| HMAC-SHA3-256 | SHA3-256 | 256ビット |
デフォルトではHMAC-SHA256を使用してください。HMAC-MD5とHMAC-SHA1は、セキュリティ重視のアプリケーションでは非推奨です。
セキュリティに関する考慮事項
鍵のサイズ — 鍵は少なくともハッシュ出力と同じ長さが必要です。HMAC-SHA256の場合は、少なくとも32バイト(256ビット)の鍵を使用してください。より長い鍵はブロックサイズまでハッシュ化されます。
鍵の秘密保持 — HMACのセキュリティは、鍵が秘密であることに完全に依存します。鍵管理システムまたは環境変数を使用してください。ソースコードに鍵をハードコードしないでください。
定数時間比較 — HMAC値の比較には、PHPの hash_equals() やPythonの hmac.compare_digest() のような定数時間比較関数を常に使用してください。タイミング攻撃により、正しいHMACが1バイトずつ漏洩する可能性があります。
リプレイ攻撃 — HMACだけではリプレイ攻撃を防げません。署名付きメッセージにタイムスタンプまたはナンスを含め、サーバー側で検証してください。
オンラインツール
Help2CodeのHMACジェネレーターは、選択したアルゴリズムと秘密鍵を使ってHMAC値を計算します。HMAC実装のテストや、開発中のテストベクトル生成に使用してください。
結論
HMACは、共有秘密鍵と暗号学的ハッシュ関数を使用してメッセージ認証を提供します。APIセキュリティ、Webhook検証、メッセージの整合性と authenticity の両方を検証する必要があるあらゆるシナリオに不可欠です。強力な秘密鍵でHMAC-SHA256を使用し、HMAC値の比較には常に定数時間関数を使用してください。