صدى
English لوحة التحكم
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. 1 اقرأ جسم الطلب بايتات خاماً — لا الـ JSON المحلَّل والمعاد ترميزه.
  2. 2 قسّم الترويسة على , و= لتحصل على t وv1.
  3. 3 احسب HMAC-SHA256(secret, t + "." + body) وحوّله إلى ست عشري.
  4. 4 قارنه بـ v1 بزمن ثابت (hash_equals، timingSafeEqual).
  5. 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 وتوقيعاً جديدين على الجسم نفسه؛ تحقق من كل تسليم على حدة.
✓ شكراً، وصلتنا ملاحظتك.