手を振るまもるくんゆるふわサイバーセキュリティ
考え中のまもるくん

HMACでAPIリクエストを署名・検証する|改ざんとなりすましを防ぐ

2026-09-09実践

#HMAC#API#Webhook#Python#実践

Stripeやgithubのwebhook、社内API——「このリクエストは本物か、途中で書き換えられていないか」を確かめるのに広く使われるのが HMAC署名です。この記事ではPythonの標準ライブラリだけでHMAC署名を実装し、改ざんとなりすましを検知するところまでやります。

HMACとは: 共有鍵でつくる"封印"

送信側と受信側が同じ秘密鍵を持ち、送信側はメッセージ+鍵からHMAC(署名)を計算して一緒に送ります。受信側は同じ計算をして一致を確認。鍵を知らない攻撃者は正しい署名を作れないので、なりすましと改ざんを同時に防げます。

💡

署名(RSA)との違い

RSAデジタル署名は公開鍵・秘密鍵のペアを使い、誰でも検証できます。HMACは1つの共有鍵を両者が持つ方式で、計算が速く実装も軽い。API内部やwebhookのように「両者が事前に鍵を共有できる」場面で使われます。

HMACの流れ

🔑

共有鍵

両者が持つ

✍️

署名

本文+鍵で計算

検証

同じ計算で照合

準備

標準ライブラリの hmachashlib だけ。追加インストール不要です。

① リクエストに署名する

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の脆弱性へどうぞ。

Footnotes

  1. Krawczyk, Bellare & Canetti (1997). RFC 2104 "HMAC: Keyed-Hashing for Message Authentication." 内外2重のハッシュで鍵付き認証を実現。

  2. 素の hash(key‖msg) は Merkle–Damgård 構造のハッシュで長さ拡張攻撃を許す。HMAC の二段構造はこれを防ぐため、自前結合よりHMACを使う。

  3. compare_digest は全バイトを比較してから返す定数時間比較。通常比較は差異位置で処理時間が変わり、タイミング攻撃の手がかりになる。

🔥 この分野の最新トレンドをチェック →

あわせて読みたい