Webhooks / التحقق من التوقيع
التحقق من التوقيع
كل webhook موقّع بسرّ التوقيع الخاص بتطبيقك. تحقق من التوقيع مقابل الجسم الخام قبل أن تثق بالبيانات، وارفض التسليمات الأقدم من خمس دقائق.
حُدّث في 21 أكتوبر 2026
عدّل هذه الصفحة
01الترويسة
X-Sada-Signature تحمل طابع unix الزمني لحظة التوقيع وتوقيع HMAC-SHA256 بالست عشري للطابع ثم نقطة ثم جسم الطلب الخام، بمفتاح هو سرّ التوقيع من لوحة التحكم ← Webhooks.
X-Sada-Signature: t=1792316400,v1=3f786850e387550fdab836ed7e6dc881de23001b4a9d1a5d2a1f8f3c7a0b9e2d
v1 = hex( HMAC-SHA256( key = signing secret, message = "<t>" + "." + <raw request body> ) )
02التحقق في خمس خطوات
الخوارزمية نفسها بأي لغة.
- 1 اقرأ جسم الطلب بايتات خاماً — لا الـ JSON المحلَّل والمعاد ترميزه.
-
2
قسّم الترويسة على
,و=لتحصل علىtوv1. -
3
احسب
HMAC-SHA256(secret, t + "." + body)وحوّله إلى ست عشري. -
4
قارنه بـ
v1بزمن ثابت (hash_equals،timingSafeEqual). -
5
ارفض التسليم حين يتجاوز
|now − t|300 ثانية؛ يحميك من إعادة الإرسال.
03الكود
حزمة Laravel تفعل كل ذلك في الميدل وير sada.webhook. وبدون حزمة هي عشرة أسطر.
routes/api.php routes/api.php server.js
1
use Illuminate\Http\Request;
2
use Sada\Laravel\Webhook;
3
4
// `sada.webhook` (Sada\Laravel\Http\Middleware\VerifyWebhookSignature) aborts 401
5
// on a bad or stale signature, so the closure only ever sees verified events.
6
Route::post('/webhooks/sada', function (Request $request) {
7
$webhook = Webhook::fromRequest($request);
8
9
$webhook->event; // "call.ended"
10
$webhook->callId; // "call_k3j9x2m7qa"
11
$webhook->data; // the `data` object as an array
12
13
return response()->noContent();
14
})->middleware('sada.webhook');
1
Route::post('/webhooks/sada', function (Request $request) {
2
// X-Sada-Signature: t=<unix>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">
3
[$t, $v1] = sscanf($request->header('X-Sada-Signature'), 't=%d,v1=%s');
4
5
$expected = hash_hmac('sha256', $t.'.'.$request->getContent(), env('SADA_WEBHOOK_SECRET'));
6
7
abort_unless(hash_equals($expected, $v1) && abs(time() - $t) < 300, 401);
8
9
$event = $request->input('event'); // call.ended, participant.joined, …
10
$call = $request->input('data.call_id');
11
12
return response()->noContent();
13
});
1
import { createHmac, timingSafeEqual } from 'node:crypto';
2
3
app.post('/webhooks/sada', express.raw({ type: 'application/json' }), (req, res) => {
4
const [t, v1] = req.get('X-Sada-Signature').split(',').map((p) => p.split('=')[1]);
5
6
const expected = createHmac('sha256', process.env.SADA_WEBHOOK_SECRET)
7
.update(`${t}.${req.body}`)
8
.digest('hex');
9
10
if (!timingSafeEqual(Buffer.from(expected), Buffer.from(v1))) return res.sendStatus(401);
11
12
const { event, data } = JSON.parse(req.body); // call.ended, data.call_id, …
13
res.sendStatus(204);
14
});
04الأخطاء الشائعة
يفشل التوقيع لأحد هذه الأسباب في كل مرة تقريباً.
- ميدل وير حلّل الجسم ووقّعت
json_encode($request->all())— ترتيب المفاتيح وتهريب Unicode يختلفان. وقّع البايتات الخام دائماً. - وكيل أو إطار عمل أعاد ترميز الجسم (الأسماء العربية تُرسل غير مهرّبة، والشرطات المائلة غير مهرّبة).
- السرّ الخطأ: سرّ التوقيع هو
whsec_…من Webhooks، لا المفتاح السري للـ API. - انحراف ساعة خادمك أكثر من خمس دقائق. زامنها بـ NTP.
- كل إعادة محاولة تحمل
tوتوقيعاً جديدين على الجسم نفسه؛ تحقق من كل تسليم على حدة.
هل كانت هذه الصفحة مفيدة؟
✓ شكراً، وصلتنا ملاحظتك.