Open in
Thibitisha sahihi za webhook
Kila ombi la webhook na zana linalotumwa na ThunderPhone husainiwa. Thibitisha sahihi mara moja kwa kutumia utaratibu ulio hapa, kisha tumia tena ukaguzi huo huo kwenye kila endpoint unayoendesha.
Kila ombi tunalotuma kwa seva yako — uwasilishaji wa webhook na
uitishaji wa endpoint za zana — hubeba sahihi ya HMAC-SHA256 katika
kichwa cha X-ThunderPhone-Signature. Fanya uthibitishaji kwa usahihi mara moja na
utumie kisadizi hichohicho katika kila kidhibiti.
Algoriti
- Soma mwili ghafi wa ombi — baiti kamili tulizotuma kwako kwa POST.
- Kokotoa
hmac_sha256(secret, body).hexdigest(). - Linganisha kwa muda usiobadilika na
X-ThunderPhone-Signature. (Ulinganishaji wa kawaida wa mifuatano huvuja taarifa za muda.)
Tunatia sahihi baiti kamili tunazotuma, kwa hivyo kuthibitisha mwili ghafi
hufanya kazi kila wakati. Baiti hizo pia ni uundaji sanifu wa JSON
wa mzigo — funguo zimepangwa kialfabeti, vitenganishi vifupi
(, na : bila nafasi), UTF-8. Hilo hukupa mbinu ya pili iliyo sawa
kabisa wakati fremu yako hutoa JSON iliyochanganuliwa pekee:
unda upya kwa muundo sanifu kisha utumie HMAC kwa huo.
# Equivalent to hashing the raw body:
import json
canonical = json.dumps(payload, separators=(",", ":"), sort_keys=True).encode("utf-8")Pendelea mwili ghafi — ni hatua moja pungufu na hauathiriwi na changamoto za ubadilishaji wa namba za JSON kwenda na kurudi katika baadhi ya lugha.
Siri ipi?
| Chanzo | Siri |
|---|---|
Endpoint ya webhook (/v1/developer/webhook-endpoints) | secret ya kila endpoint (herufi 48 za heksadesimali) inayorejeshwa mara moja wakati wa kuunda |
| Webhook ya zamani ya URL moja | secret ya kila shirika inayorejeshwa kwenye GET /v1/webhook |
Uitishaji wa endpoint ya zana (mwito wa moja kwa moja kwa endpoint.url yako) | Siri ya webhook ya kiwango cha shirika (sawa na ile ya webhook ya zamani ya URL moja) — si siri ya kila endpoint |
Hifadhi siri katika kidhibiti chako cha siri au kigeu cha mazingira — usiiweke kamwe kwenye commit.
Utekelezaji wa marejeleo
Zote nne huthibitisha mwili ghafi wa ombi:
import hashlib
import hmac
def verify(body: bytes, signature: str, secret: str) -> bool:
"""Constant-time HMAC-SHA256 verification."""
expected = hmac.new(
secret.encode("utf-8"),
body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, signature or "")import crypto from "node:crypto";
export function verify(body, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex");
if (!signature || expected.length !== signature.length) return false;
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature),
);
}package webhook
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
func Verify(body []byte, signature, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(body)
expected := hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signature))
}require "openssl"
def verify(body, signature, secret)
expected = OpenSSL::HMAC.hexdigest("SHA256", secret, body)
Rack::Utils.secure_compare(expected, signature.to_s)
endUunganishaji mahususi wa framework
from fastapi import FastAPI, HTTPException, Request
app = FastAPI()
@app.post("/thunderphone-webhook")
async def hook(request: Request):
body = await request.body() # raw bytes, NOT request.json()
sig = request.headers.get("X-ThunderPhone-Signature", "")
if not verify(body, sig, SECRET):
raise HTTPException(status_code=401)
import json
event = json.loads(body)
# … dispatch on event["type"] …
return {"ok": True}import express from "express";
const app = express();
app.post(
"/thunderphone-webhook",
// IMPORTANT: parse as raw; do NOT use express.json() here.
express.raw({ type: "application/json" }),
(req, res) => {
const sig = req.header("X-ThunderPhone-Signature") || "";
if (!verify(req.body, sig, process.env.WEBHOOK_SECRET)) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body.toString("utf8"));
// … dispatch on event.type …
res.sendStatus(204);
},
);import json
from django.http import JsonResponse, HttpResponseForbidden
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
@csrf_exempt
@require_POST
def hook(request):
body = request.body # raw bytes
sig = request.headers.get("X-ThunderPhone-Signature", "")
if not verify(body, sig, SECRET):
return HttpResponseForbidden("invalid signature")
event = json.loads(body)
# … dispatch on event["type"] …
return JsonResponse({"ok": True})Kuthibitisha miito ya zana
Ejenti inapoitisha moja kwa moja mojawapo ya
zana zako za function (zana ina
endpoint), ombi hubeba header mbili za ThunderPhone pamoja na
endpoint.headers ulizosanidi:
X-ThunderPhone-Call-ID— kitambulisho cha nambari cha simu inayoendelea.X-ThunderPhone-Signature— HMAC-SHA256, yenye ufunguo wa siri ya webhook ya kiwango cha shirika, juu ya baiti halisi za mwili wa ombi.
Kisaidizi kilekile cha verify() hufanya kazi bila mabadiliko, kwa mambo mawili ya kuzingatia:
- Zana za
GET/DELETEhazina mwili. Hoja hupitishwa kama vigezo vya query, na sahihi huhesabiwa juu ya mfuatano tupu wa baiti — kwa hivyoverify(b"", sig, secret)(Python) auverify(Buffer.alloc(0), sig, secret)(Node). Usihesabu hash ya mfuatano wa query. - Mashirika yasiyo na webhook ya zamani iliyosanidiwa hayana siri ya shirika. Katika
hali hiyo miito ya zana hubeba
X-ThunderPhone-Call-IDpekee na haina header ya sahihi. Sanidi webhook ya zamani (PUT /v1/webhook) ili kupata siri ya kutia sahihi, au thibitisha miito ya zana kwa header yako mwenyewe kupitiaendpoint.headers.
@app.post("/tools/search-appointments")
async def tool(request: Request):
body = await request.body() # b"" for GET/DELETE tools
sig = request.headers.get("X-ThunderPhone-Signature", "")
call_id = request.headers.get("X-ThunderPhone-Call-ID", "")
if not verify(body, sig, ORG_WEBHOOK_SECRET):
raise HTTPException(status_code=401)
args = json.loads(body)
...Usambazaji wa zana katika hali ya webhook (zana zisizo na endpoint, zinazoletwa
kwenye webhook ya shirika lako kama telephony.tool / web.tool) ni webhook ya kawaida
iliyotiwa sahihi — utaratibu wa kawaida hapo juu unatumika. Tazama
Zana za Function kwa miundo yote miwili ya ombi.
Changamoto za kawaida
Kusajili upya kwa uumbizaji chaguo-msingi
Kuchanganua mwili na kuutoa tena kwa chaguo-msingi za maktaba yako ya JSON
(nafasi baada ya , / :, funguo zilizo kwa mpangilio wa uingizaji) huzalisha
baiti tofauti na kuharibu HMAC. Thibitisha mwili ghafi — au ikiwa ni lazima
uusajili upya, linganisha muundo wetu wa kanoni hasa: funguo zilizopangwa,
vitenganishi fupi, UTF-8.
Framework huchanganua JSON kiotomatiki
Middleware ya express.json() ya Express hutumia mtiririko wa mwili
na unapoteza baiti ghafi. Tumia express.raw() mahsusi kwenye njia ya webhook,
au hifadhi mwili ghafi kwenye pre-middleware.
Hali ni sawa kwa NestJS / Koa — angalia nyaraka zao za "raw body".
Ulinganisho usio salama kwa muda
expected === signature katika JS au expected == signature katika
Python ni ulinganisho unaobadilika kulingana na muda. Tumia crypto.timingSafeEqual
au hmac.compare_digest mtawalia. Tofauti ya utendaji haipo.
Siri isiyo sahihi kwa endpoint za zana
Miito ya moja kwa moja ya endpoint za zana husainiwa kwa siri ya webhook
ya kiwango cha shirika (GET /v1/webhook) — si kwa siri yoyote ya kila endpoint
kutoka /v1/developer/webhook-endpoints. Tumia tena kitendakazi kilekile cha verify(),
lakini hakikisha unakipa siri ya shirika kwenye njia za zana.
Kuheshi mfuatano wa hoja kwenye zana za GET/DELETE
Kwa mbinu za zana zisizo na mwili, sahihi hufunika mfuatano tupu wa baiti, ikidumisha utaratibu mmoja wa jumla: HMAC mwili ghafi wa ombi, wowote ulivyo. Kuheshi URL au mfuatano wa hoja hakutalingana kamwe.
Kutorejesha 401 kunapokuwa na kutolingana
Kurejesha 200 uthibitishaji unaposhindwa hufanya kishughulikiaji kuwa lengo la mashambulizi ya kurudia ombi. Daima jibu kwa hali isiyo ya 2xx uthibitishaji unaposhindwa.