
HMACでAPIリクエストを署名・検証する|改ざんとなりすましを防ぐ
2026-09-09 ・ 実践
Stripeやgithubのwebhook、社内API——「このリクエストは本物か、途中で書き換えられていないか」を確かめるのに広く使われるのが HMAC署名です。この記事ではPythonの標準ライブラリだけでHMAC署名を実装し、改ざんとなりすましを検知するところまでやります。
HMACとは: 共有鍵でつくる"封印"
送信側と受信側が同じ秘密鍵を持ち、送信側はメッセージ+鍵からHMAC(署名)を計算して一緒に送ります。受信側は同じ計算をして一致を確認。鍵を知らない攻撃者は正しい署名を作れないので、なりすましと改ざんを同時に防げます。
署名(RSA)との違い
RSAデジタル署名は公開鍵・秘密鍵のペアを使い、誰でも検証できます。HMACは1つの共有鍵を両者が持つ方式で、計算が速く実装も軽い。API内部やwebhookのように「両者が事前に鍵を共有できる」場面で使われます。
HMACの流れ
共有鍵
両者が持つ
署名
本文+鍵で計算
検証
同じ計算で照合
準備
標準ライブラリの hmac・hashlib だけ。追加インストール不要です。
① リクエストに署名する
import hmac, hashlib
SECRET = b"our-shared-secret-key"
def sign(body: bytes) -> str:
return hmac.new(SECRET, body, hashlib.sha256).hexdigest()
body = b'{"amount": 1000, "to": "Bob"}'
signature = sign(body)
print("署名:", signature)
送信側はこの signature をHTTPヘッダ(例: X-Signature)に入れて送ります。
② 受信側で検証する
def verify(body: bytes, received_sig: str) -> bool:
expected = sign(body)
return hmac.compare_digest(expected, received_sig) # タイミング攻撃対策
print(verify(body, signature)) # True
# 本文を1文字でも書き換えると…
print(verify(b'{"amount": 9999, "to": "Bob"}', signature)) # False
正しい本文と署名の組だけ True。金額を1000→9999に改ざんすると即 False。鍵を知らない攻撃者は、書き換えた本文に合う署名を作れません。
必ず compare_digest で比較
署名の照合に == を使うと、一致した文字数で処理時間が変わり、攻撃者に手がかりを与えます(タイミング攻撃)。hmac.compare_digest は比較時間を一定に保ちます。HMAC検証では必須の作法です。
③ リプレイ攻撃も塞ぐ
HMACは改ざんを防ぎますが、「正しいリクエストをそのまま再送する」リプレイ攻撃は防げません。実務ではタイムスタンプを署名対象に含め、古いリクエストを弾きます。
import time
def sign_with_ts(body: bytes):
ts = str(int(time.time())).encode()
sig = hmac.new(SECRET, ts + b"." + body, hashlib.sha256).hexdigest()
return ts.decode(), sig
def verify_with_ts(body, ts, sig, max_age=300):
if abs(time.time() - int(ts)) > max_age: # 5分より古ければ拒否
return False
expected = hmac.new(SECRET, ts.encode() + b"." + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, sig)
ts, sig = sign_with_ts(body)
print(verify_with_ts(body, ts, sig)) # True(発行直後なので有効)
タイムスタンプごと署名するのがポイント。署名対象にタイムスタンプを含めれば、それ自体も改ざんできません。
まとめ
- HMAC=共有鍵でメッセージに署名し、受信側が同じ計算で検証する
- 改ざん(本文の書き換え)となりすまし(鍵を知らない相手)を同時に防ぐ
- 照合は
hmac.compare_digestでタイミング攻撃を防ぐ - リプレイ攻撃はタイムスタンプを署名に含めて弾く
もう少し詳しく(背景と理論)
HMAC は Bellare・Canetti・Krawczyk (1996) が提案し RFC 2104 で標準化された、ハッシュ関数に鍵を組み込むメッセージ認証方式です1。単に hash(secret + message) とするのは危険で、多くのハッシュ関数(MD5/SHA-1/SHA-256 の系列)は長さ拡張攻撃を許してしまいます。HMAC は内側・外側の2段構えでこれを防ぎます2。検証時は必ず hmac.compare_digest のような定数時間比較を使うこと——通常の == は不一致箇所で早期に返るため、応答時間の差から署名を推測するタイミング攻撃の糸口になります3。
次の一歩 🌸
公開鍵方式の署名はRSAデジタル署名の実装、通信の暗号化はHTTPSのしくみ、Webの弱点はWebの脆弱性へどうぞ。