StableOps
SDK

Webhook 驗籤

瞭解如何使用 @stableops/webhook 在 Node.js、邊緣執行時和其他 JavaScript 環境中驗證 StableOps Webhook 簽名,正確保留原始請求體、讀取時間戳與簽名頭、限制重放視窗,並在金鑰輪換期間安全相容新舊簽名。

簽名內容

每次投遞攜帶三個 header:

X-Product-Signature: t=1780301011,v1=7b11b2c1…
X-Event-Id: evt_01JYA…
X-Delivery-Id: del_01JYA…

簽名為 HMAC-SHA256(secret, "${timestamp}.${rawBody}"),hex 編碼。 5 分鐘時間窗防重放;簽名比較常量時間,避免計時側通道。

在 handler 中驗證

SDK 直接 re-export 驗籤函式,框架使用者不需要額外依賴:

import { verifySignature, SIGNATURE_HEADER } from '@stableops/api-sdk/webhooks'

export async function POST(req: Request) {
  // 必須用 *原始* body 驗籤。不要先 JSON.parse 再 stringify,
  // 即便對同一物件,key 順序也可能變化。
  const rawBody = await req.text()

  const result = verifySignature({
    secrets: [process.env.STABLEOPS_WEBHOOK_SECRET!],
    header: req.headers.get(SIGNATURE_HEADER.toLowerCase()) ?? undefined,
    rawBody,
  })

  if (!result.ok) {
    return new Response(JSON.stringify({ reason: result.reason }), {
      status: 400,
    })
  }

  const event = JSON.parse(rawBody)
  // 業務邏輯,按 event.id 冪等
  return new Response('ok')
}

金鑰輪換

輪換介面立即返回新 secret,並保留舊 secret 24 小時。期間把兩條 secret 同時 傳給 verifySignature

verifySignature({
  secrets: [
    process.env.STABLEOPS_WEBHOOK_SECRET!,
    process.env.STABLEOPS_WEBHOOK_SECRET_PREVIOUS!,
  ],
  header,
  rawBody,
})

失敗原因

verifySignature 返回判別聯合,ok: false 時讀取 reason

reason含義
missing_header缺失 X-Product-Signature
invalid_formatheader 不符合 t=…,v1=…
timestamp_expired超出 5 分鐘視窗
bad_signatureHMAC 與任一 secret 都不匹配

重複 delivery

驗籤透過後,按 X-Event-Id(推薦)或自家業務鍵去重。平台的重試排程 + 網路重試,意味著 同一事件可能落多次。handler 必須冪等。

Edge / Serverless

verifySignature 使用 node:cryptocreateHmactimingSafeEqualBuffer。 主流 edge 執行時都透過原生或相容層支援 node:crypto:Deno、Vercel 原生支援, 無需額外 polyfill;Cloudflare Workers 需在 wrangler.toml 中啟用 nodejs_compat 相容性標誌後即可使用。

這篇文件怎麼樣?

最後更新

本頁內容