Skip to main content
我們傳送至你伺服器的每個請求——Webhook 傳送和工具端點呼叫——都會在 X-ThunderPhone-Signature 標頭中附帶 HMAC-SHA256 簽章。一次正確完成驗證,並將同一個輔助函式套用至所有處理常式。

演算法

  1. 讀取原始請求本文——也就是我們 POST 給你的確切位元組。
  2. 計算 hmac_sha256(secret, body).hexdigest()
  3. 固定時間方式與 X-ThunderPhone-Signature 比較。 (直接比較字串會洩漏計時資訊。)
我們簽署的是實際傳輸的確切位元組,因此驗證原始本文一定可行。這些位元組同時也是承載資料的正規 JSON 序列化——金鑰依字母順序排序、使用緊湊分隔符號(,:,不含空格)、UTF-8。當你的框架僅提供已解析的 JSON 時,這也提供另一種完全等效的方法:以正規格式重新序列化,再對其計算 HMAC。
優先使用原始本文——可少一個步驟,且不受某些語言中 JSON 數值往返轉換細微差異的影響。

使用哪個密鑰?

將密鑰儲存在你的密鑰管理工具或環境變數中——絕不要提交至版本控制。

參考實作

以下四種實作皆會驗證原始請求本文:

特定框架整合

驗證工具呼叫

當智慧體直接呼叫你的其中一個 函式工具(該工具具有 endpoint)時,請求會連同你設定的 endpoint.headers 攜帶兩個 ThunderPhone 標頭:
  • X-ThunderPhone-Call-ID ——進行中通話的數字 ID。
  • X-ThunderPhone-Signature ——使用你的組織層級 webhook 密鑰作為金鑰,針對完全相同的請求本文位元組計算的 HMAC-SHA256。
相同的 verify() 輔助函式可直接使用,但有兩點差異:
  1. GET / DELETE 工具沒有本文。 引數會透過查詢參數傳遞,簽章則針對空位元組字串計算——因此請使用 verify(b"", sig, secret)(Python)或 verify(Buffer.alloc(0), sig, secret)(Node)。不要雜湊查詢字串。
  2. 未設定舊版 webhook 的組織沒有組織密鑰。 在這種情況下,工具呼叫只會攜帶 X-ThunderPhone-Call-ID,不會有簽章標頭。設定舊版 webhook(PUT /v1/webhook)以取得簽署密鑰,或透過 endpoint.headers 使用你自己的標頭驗證工具呼叫。
Webhook 模式的工具分派(沒有 endpoint 的工具,會以 telephony.tool / web.tool 傳送至你的組織 webhook)屬於一般的已簽署 webhook——適用上述的標準做法。請參閱 函式工具 了解兩種請求格式。

常見陷阱

解析請求主體後,再以 JSON 函式庫的預設格式重新輸出(在 , / : 後加入空格、依插入順序排列金鑰),會產生不同的位元組並導致 HMAC 驗證失敗。請驗證原始請求主體——或者若你必須重新序列化,請完全符合我們的標準格式:排序後的金鑰、精簡分隔符號、UTF-8。
Express 的 express.json() 中介軟體會取用請求主體串流,讓你失去原始位元組。請在 webhook 路由上專門使用 express.raw(),或在前置中介軟體中緩衝原始請求主體。NestJS / Koa 也是相同情況——請查看它們的「原始請求主體」文件。
JS 中的 expected === signature 或 Python 中的 expected == signature 都是耗時會變動的比較方式。請分別使用 crypto.timingSafeEqualhmac.compare_digest。效能差異可忽略不計。
直接呼叫工具端點時,會使用 組織層級的 webhook 密鑰GET /v1/webhook)簽署——而非 /v1/developer/webhook-endpoints 中任何個別端點的密鑰。請重複使用相同的 verify() 函式,但請確認你在工具路由中傳入的是組織密鑰。
對於沒有請求主體的工具方法,簽章涵蓋的是空位元組字串,以維持單一通用流程:無論原始請求主體為何,都對它計算 HMAC。對 URL 或查詢字串進行雜湊永遠不會相符。
驗證失敗時回傳 200,會讓處理常式成為重放攻擊的目標。驗證失敗時,請一律回應非 2xx 狀態碼。

後續步驟

Webhook 概覽

傳遞語意、重試、來源 IP 位址。

Webhook 端點

管理多個 URL、輪替密鑰。

函式工具

兩種工具呼叫路徑及其請求格式。

工具整合

從頭到尾建立完整的工具支援整合。