HMAC 生成・署名検証
メッセージと鍵から HMAC-SHA-1、SHA-256、SHA-384、SHA-512 を計算し、与えられた署名がどれによるものかを判定します。
署名を計算するには鍵を入力してください。
このツールの役割
HMAC はメッセージと共有シークレットを短い署名に変えます。同じシークレットを持つ者は誰でもそれを計算し直し、メッセージが変わらず届き、シークレットを知る誰かから来たかを確かめられます。このツールはよく使う 4 つの変種を一度に計算し — 送られてきた署名を与えれば — どれがそれを生んだかを教えます。
その 2 つ目の向きこそ、たいてい人が訪れる理由です。Webhook が検証に失敗していて、問いは実は「この署名は有効か」ではなく「もっともらしいいくつかのことのうち、送信者と違うことを私はどれでしているか」です。
うまくいかないのは鍵
HMAC はバイトをバイトで署名します。メッセージはたいてい明白 — 生のリクエスト本文 — ですが、鍵はほぼ決してそうではありません。シークレットは文字列として届き、文字列はどう読むかを決めるまではバイトではないからです:
a3f2 テキストとして 4 バイト 61 33 66 32 a3f2 16進数として 2 バイト a3 f2
どちらの読みも正当で、どちらも申し分なく整った署名を生み、二つの署名には共通点がありません。何も警告しません: エラーも、長さの苦情もなく、ただ送信者が計算したものと一致しない値があるだけです。だからここでの鍵のエンコードは推測ではなく見える選択です — 署名が一致しないとき、まず切り替えるのはこれです。
大まかな目安として: whsec_ のような接頭辞や大小混じりの文字と数字の連なりを持つシークレットは、ふつうテキストとして意図されています。ちょうど 32 文字か 64 文字で 0-9 と a-f だけを使う文字列は、たいてい 16 進数です。= で終わるものは、ほぼ確実に Base64 です。ただし形は証明ではないので、形ではなく送信者の文書を確認してください。
メッセージは正確なバイトでなければならない
不一致のもう半分はメッセージです。HMAC はバイトの上に定義されるので、バイトを変えるものは何であれ署名を完全に変えます — 部分点も、惜しい一致もありません。
- JSON の再直列化。本文を解析して再び文字列化すると、キーが並べ替わり、間隔が変わり、数値が正規化されることがあります。受け取った生の本文に署名して検証し、決してその往復した複製にはしないでください。
- 末尾の改行。本文をファイルに保存すると加えるツールがあります。それはバイトで、すべてを変えます。
- 文字エンコード。非 ASCII テキストを含む本文は、両側が使う同じエンコード — 実際には UTF-8 — として読まれなければなりません。
- 圧縮やミドルウェア。ハンドラの前の何かが本文を展開したり書き換えたりするなら、その後ではなく前に検証してください。
多くのプロバイダーは本文だけに署名するのでもありません。Stripe はタイムスタンプと本文をドットで結んで署名し、AWS はメソッド・パス・ヘッダーとペイロードのハッシュから作った正規リクエストに署名します。素の本文に対して検証が失敗するなら、署名された文字列はおそらく素の本文ではありません — それは送信者側に文書化されており、さらにデバッグする前に読む価値があります。
SHA-1 がハッシュツールでなくここで提供される理由
ハッシュツールは SHA-1 を破られていると印付けします。こちらは HMAC-SHA-1 を警告なしに載せ、それは見落としではなく意図的です。
SHA-1 は署名と証明書には使えません。衝突を構成できるからです: 二つの異なる文書がダイジェストを共有するようにできます。HMAC はその性質に依存しません。その安全性はシークレット鍵に基づき、構成 — メッセージを二度、両方に鍵を混ぜてハッシュする — は、下地のハッシュが衝突に弱くても持ちこたえます。HMAC-SHA-1 は今も健全で、OAuth 1.0a や古い AWS 署名が今も使うものなので、それを計算するのを拒むツールは、より安全になることなく、ただ役に立たなくなるだけです。
新しいものには何であれ SHA-256 が妥当な既定です。長いダイジェストはここで意味のあるほど強くはなく — 安全性の天井はダイジェストの長さではなく鍵です — ので、SHA-384 と SHA-512 は、相互運用しなければならない何かがそれを求めるときにだけ選ぶ価値があります。
署名を安全に比べる
このページはふつうの文字列比較で比べ、ここではそれで問題ありません: あなた自身が鍵を持っているので、漏らすものは何もありません。入ってくるリクエストを検証するサーバーでは、それでは問題があります。ふつうの比較は最初の異なるバイトで止まるので、かかる時間が推測のどれだけが正しかったかを明かし、多くのリクエストを送れる攻撃者が有効な署名を 1 バイトずつ復元できます。
プラットフォームが提供する定数時間の比較 — Node の crypto.timingSafeEqual、Python の hmac.compare_digest、PHP の hash_equals — を、16 進数の文字列ではなく生のバイトに対して使ってください。それとともに、リクエストで名指しされたアルゴリズムを信じるのではなく自分で計算した署名に対して比べ、古い有効な署名が再生されないよう、タイムスタンプが今から遠いメッセージを拒んでください。
HMAC はハッシュでも署名でもない
ここで三つのものが混同され、その違いは後で何を主張できるかにとって重要です:
- ハッシュはメッセージを取ってダイジェストを生みます。誰でも計算できるので、メッセージが偶然に変わっていないことは証明しますが — 特定の誰かから来たことは証明しません。
- HMAC はメッセージと共有シークレットを取ります。両当事者が同じ鍵を持つので、送信者がシークレットを知っていたことを証明します。どちらの当事者が送ったかは証明できません。どちらも生めたからです。
- 署名は、送信者だけが持つ秘密鍵と、誰でも検証できる公開鍵を使います。それが否認防止を与えます: 送信者は後でそれを否定できません。
だから HMAC は、既にシークレットを共有する二つのシステムの間 — Webhook、内部 API、セッショントークン — で正しいツールであり、第三者に誰が何を送ったかを証明する必要があるときには誤ったツールです。また、認証はしても隠しはしないことに注意してください: メッセージは平文で伝わり、HMAC は機密性について何も言いません。
よくある質問
- 署名が一致しません。まず何を確認しますか?
- 鍵のエンコード、次にメッセージのバイトです。16 進数に見えるシークレットはしばしばテキストではなく 16 進数として意図され、二つはどちらもエラーなしにまったく異なる署名を生みます。その後、再直列化した複製ではなく受け取ったとおりの生の本文に署名していることを確かめてください。
- ハッシュツールが破られていると言う SHA-1 が、ここで提供されるのはなぜですか?
- HMAC が、SHA-1 が失った性質である衝突耐性に頼らないからです。その安全性は鍵から来ます。HMAC-SHA-1 は今も健全で、OAuth 1.0a や古い AWS 署名で今も使われていますが、新しいものには SHA-256 が正しい既定です。
- 長いダイジェストのほうが安全ですか?
- 意味のあるほどではありません。HMAC の強さはダイジェストの長さではなくシークレットで区切られるので、SHA-512 は SHA-256 の 4 倍良いわけではありません。相手が期待するものを選んでください。
- HMAC とデジタル署名の違いは何ですか?
- HMAC は両側が知る一つのシークレットを使うので、送信者がシークレットを知っていたことは証明しますが、どちら側が送ったかは証明しません。デジタル署名は送信者だけが持つ秘密鍵を使うので、第三者が検証でき、送信者は否定できません。その最後の性質が必要なら、HMAC は誤ったツールです。
- 署名を === で比べるべきですか?
- サーバーではだめです。ふつうの比較はバイトが異なるとすぐ返り、そのタイミングが推測した署名のどれだけが正しかったかを漏らします。プラットフォームの定数時間の比較を生のバイトに使ってください。このページでは、既に鍵を持っているので問題ありません。
- 私の鍵はどこかに送られますか?
- いいえ。すべてが Web Crypto API でブラウザ内で計算されます。メッセージも鍵も端末の外に出ることはありません。