ホーム/ インサイト/ Standard Webhooks とは|Webhook の署名と検証の共通仕様

Standard Webhooks とは|Webhook の署名と検証の共通仕様

Webhook を受け取る側は、届いたリクエストが本当に送信元のサービスから来たものかを署名で確かめます。ところが署名の形式はサービスごとに違い、ヘッダー名、署名する文字列、鍵の扱いを連携先ごとに調べて実装してきました。Standard Webhooks は、この署名とヘッダーの形式をそろえるための公開仕様です。ここでは仕様の中身と、受信側での署名検証の実装を整理します。

本記事の内容は 2026 年 9 月時点の仕様(バージョン 1.0.0)と各社の公開情報です。出典は末尾にまとめています。

Standard Webhooks の概要

Standard Webhooks は、Webhook の送信サービスを提供する Svix が中心となってまとめ、2023 年 12 月に公開した仕様です。仕様とライブラリは GitHub で公開されており、仕様は Apache License 2.0 です。

運営は技術運営委員会が担い、2026 年 9 月時点の委員は Zapier、Twilio、Lob、Mux、ngrok、Supabase、Svix、Kong の各社のエンジニアです。

仕様が解決しようとしているのは、Webhook の実装がサービスごとに異なる点です。受信側は連携先が増えるたびに署名の検証方法を調べ直し、送信側は同じ課題をそれぞれ設計してきました。Standard Webhooks は、署名の方式・ヘッダー・本文の形・再送の考え方を 1 つの仕様にまとめています。

3 つのヘッダー

Standard Webhooks に沿った Webhook は、本文と一緒に次の 3 つのヘッダーを送ります。

ヘッダー内容例
webhook-idイベントごとの一意な ID。再送しても変わらないmsg_2KWPBgLlAfxdpx2AI54pPJ85f4W
webhook-timestamp送信を試みた時刻(UNIX 時間の秒)1674087231
webhook-signature署名。複数あるときは空白で区切るv1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=

webhook-id はイベントに付く ID で、再送のたびに同じ値が届きます。webhook-timestamp は送信を試みた時刻で、再送のたびに新しくなります。

署名の計算方法

署名には対称鍵(v1)と非対称鍵(v1a)の 2 つの方式があります。

対称鍵(v1)

送信側と受信側が同じ鍵を持つ方式で、HMAC-SHA256 を使います。手順は次のとおりです。

  1. webhook-id、webhook-timestamp、本文をピリオドでつないだ文字列を作る(msg_xxx.1674087231.{"type":"order.paid",...})
  2. 鍵で HMAC-SHA256 を計算し、結果を base64 にする
  3. 先頭に v1, を付けて webhook-signature に入れる

鍵は 24〜64 バイトのランダムな値を base64 にし、先頭に whsec_ を付けた形で受信側に渡します。計算に使うのは whsec_ を除いて base64 から戻したバイト列です。

非対称鍵(v1a)

Ed25519 の鍵ペアを使う方式です。送信側が秘密鍵(whsk_ で始まる)で署名し、受信側は公開鍵(whpk_ で始まる)で検証します。受信側に秘密の値を渡さずに済むため、仕様は非対称鍵を推奨しています。一方で、計算の負荷は対称鍵より高くなります。

受信側の確認事項

仕様は、署名の一致に加えて次の点を確かめるよう求めています。

  • 時刻の許容範囲:webhook-timestamp が現在時刻から一定の範囲内かを確かめ、過去のリクエストの再利用(リプレイ攻撃)を防ぎます。公式ライブラリの JavaScript 版は前後 5 分を範囲としています
  • 重複の排除:webhook-id を冪等キーとして記録し、同じイベントを 2 回処理しないようにします。再送やネットワークの都合で、同じイベントが複数回届くことがあるためです
  • 一定時間での比較:対称鍵の署名は、比較にかかる時間が内容で変わらない関数で比べます。通常の文字列比較では、タイミング攻撃の対象になります
  • 受け取ったままの本文:本文を JSON として読み込んでから文字列に戻すと、空白や並び順が変わって署名が一致しなくなります。検証には受け取ったバイト列をそのまま使います

鍵の切り替え

webhook-signature に複数の署名を入れられるのは、鍵を止めずに切り替えるためです。送信側は切り替え後の一定期間、新しい鍵と古い鍵の両方で署名し、空白で区切って送ります。受信側はどちらかの署名が一致すれば受け付けるため、この併用期間のうちに新しい鍵へ更新すれば、受信を止めずに切り替えられます。

webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4= v1,PGOol9edfeMgYBaSh+rI9ThEyc0IoEXMU2Xteb3Pl68=

公式ライブラリ

署名の作成と検証を行う公式のライブラリが、次の 9 言語で公開されています。

  • Python(PyPI の standardwebhooks)
  • JavaScript/TypeScript(npm の standardwebhooks)
  • Java/Kotlin(Maven Central の com.standardwebhooks:standardwebhooks)
  • Rust(crates.io の standardwebhooks)
  • Go、Ruby、PHP、C#、Elixir

このほか、Haskell や Swift などのコミュニティによる実装もあります。

署名検証のコード例(Node.js)

公式ライブラリを使う場合

Express で受け取る例です。express.raw() で本文をバイト列のまま受け取り、verify に渡します。署名が一致しない場合や時刻が範囲外の場合は例外になります。

import express from 'express';
import { Webhook } from 'standardwebhooks';

const wh = new Webhook(process.env.WEBHOOK_SECRET); // whsec_ で始まる鍵
const app = express();

app.post('/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
  let event;
  try {
    event = wh.verify(req.body.toString('utf8'), req.headers);
  } catch {
    return res.status(400).end();
  }
  // webhook-id で処理済みかを確かめてから処理する
  console.log(req.headers['webhook-id'], event.type);
  res.status(204).end();
});

app.listen(3000);

ライブラリを使わない場合

Node.js 標準の crypto だけで書くと次のようになります。時刻の確認、複数の署名への対応、一定時間での比較を含めています。

import crypto from 'node:crypto';

function verify(secret, headers, body, toleranceSec = 300) {
  const id = headers['webhook-id'];
  const ts = headers['webhook-timestamp'];
  const sigHeader = headers['webhook-signature'];
  if (!id || !ts || !sigHeader) return false;
  if (Math.abs(Math.floor(Date.now() / 1000) - Number(ts)) > toleranceSec) return false;

  const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64');
  const expected = crypto.createHmac('sha256', key).update(`${id}.${ts}.${body}`).digest();

  return sigHeader.split(' ').some((s) => {
    const [ver, sig] = s.split(',');
    if (ver !== 'v1' || !sig) return false;
    const got = Buffer.from(sig, 'base64');
    return got.length === expected.length && crypto.timingSafeEqual(got, expected);
  });
}

body には受け取ったままの本文の文字列を渡します。

対応している主なサービス

Standard Webhooks の公式サイトは、対応しているサービスとして OpenAI、Anthropic、Google Gemini、Twilio、Kong、PagerDuty、Supabase などを挙げています。各社の開発者向け資料での扱いは次のとおりです。

  • OpenAI:Webhook が Standard Webhooks の仕様に沿っており、公式ライブラリで検証できると明記しています
  • Supabase:認証の HTTP フックが Standard Webhooks の仕様を実装しており、検証の例に standardwebhooks を使っています
  • Anthropic:Claude Managed Agents の Webhook が webhook-id・webhook-timestamp・webhook-signature の 3 つのヘッダーと whsec_ で始まる鍵を使い、5 分を超えた配信は SDK の検証で拒否されます

複数のサービスの Webhook を受け取る場合でも、検証の処理を 1 つにまとめられます。

送信側の実装

当社の Webhook 統合インテグレーション「Webhook Admin」は、Standard Webhooks 形式(v1)の署名を付けて Webhook を送信します。送信先ごとに whsec_ で始まる鍵を発行し、鍵を切り替えた後の 24 時間は新旧両方の鍵で署名します。受信側は本記事のコードや公式ライブラリでそのまま検証できます。再送、配信記録、障害の通知も含めて、送信側の仕組みを自社で作らずに Standard Webhooks に対応できます。

出典

よくある質問

Standard Webhooks の署名はどの言語で検証できますか?

公式のライブラリが Python・JavaScript/TypeScript・Java/Kotlin・Rust・Go・Ruby・PHP・C#・Elixir の 9 言語にあります。Node.js では npm の standardwebhooks を使い、Webhook クラスの verify に受け取った本文とヘッダーを渡します。

署名検証が失敗する原因で多いものは何ですか?

受け取った本文を JSON として読み込み、もう一度文字列に直してから検証することです。空白や並び順が 1 文字でも変わると署名は一致しません。検証には受け取ったままのバイト列を使います。

独自形式の署名から Standard Webhooks に移行できますか?

仕様では、既存のヘッダーを残したまま Standard Webhooks のヘッダーを追加する移行方法を示しています。署名用の鍵も既存のものを使えるため、受信側は準備ができた順に新しい形式の検証へ切り替えられます。

ご相談・お問い合わせ

お問い合わせをいただいてから、営業日2日以内にご返信いたします。