شرح Webhooks: الحمولات الموقعة وإعادة المحاولة

Backend2026-10-09TryQuickToolBox

لقد قمت للتو بدمج بوابة دفع، والآن تحتاج إلى تحديث قاعدة البيانات الخاصة بك عند نجاح الدفع. الاستعلام المتكرر عن API كل بضع ثوانٍ يعد مضيعة للوقت وبطيئًا. تحل Webhooks هذه المشكلة عن طريق دفع الأحداث إلى خادمك فور حدوثها. ولكن بدون الأمان والموثوقية المناسبين، يمكن أن تصبح webhooks مصدرًا للأخطاء والثغرات الأمنية. يشرح هذا المقال كيفية تنفيذ webhooks بشكل صحيح، مع التركيز على الحمولات الموقعة وإعادة المحاولة.

ما هي Webhooks؟

webhook هو استدعاء HTTP: يحدث حدث في خدمة ما، وترسل تلك الخدمة طلب HTTP POST إلى عنوان URL تقدمه. تحتوي الحمولة عادةً على بيانات JSON حول الحدث. على سبيل المثال، عندما يكمل العميل عملية دفع، يرسل Stripe حدث charge.succeeded إلى نقطة النهاية الخاصة بك.

تعتمد webhooks على الدفع، لذا تقلل من زمن الاستجابة وحمل الخادم مقارنة بالاستعلام المتكرر. ومع ذلك، فإنها تقدم تحديات: كيف تعرف أن الطلب حقيقي؟ ماذا لو كان خادمك متوقفًا عند إطلاق الحدث؟

لماذا تهم الحمولات الموقعة

يمكن لأي شخص إرسال HTTP POST إلى نقطة نهاية webhook الخاصة بك. بدون التحقق، يمكن للمهاجم تزوير الأحداث، مما يؤدي إلى إجراءات غير مصرح بها مثل وضع علامة على الطلب كمدفوع. تحل الحمولات الموقعة هذه المشكلة عن طريق تضمين توقيع تشفيري لا يمكن إنشاؤه إلا من قبل المرسل وأنت.

الطريقة الأكثر شيوعًا هي HMAC (رمز المصادقة على الرسائل القائم على التجزئة) مع سر مشترك. يحسب المرسل تجزئة للحمولة باستخدام السر ويضمّنها في رأس الطلب. تقوم بإعادة حساب التجزئة ومقارنتها. إذا تطابقت، فإن الطلب أصلي.

كيفية التحقق من حمولة موقعة

إليك نهجًا خطوة بخطوة باستخدام Node.js و Express:

  1. استرجع جسم الطلب الخام كسلسلة نصية. لا تقم بتحليله كـ JSON قبل التحقق، لأن التحليل قد يغير تسلسل البايتات.
  2. استخرج التوقيع من الرأس (مثل X-Signature).
  3. احسب HMAC باستخدام السر الخاص بك والجسم الخام.
  4. قارن التوقيع المحسوب مع التوقيع المستلم باستخدام مقارنة ثابتة الزمن لمنع هجمات التوقيت.
const crypto = require('crypto');

function verifySignature(rawBody, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
  const signature = req.headers['x-signature'];
  if (!verifySignature(req.body, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  const event = JSON.parse(req.body);
  // Process event
  res.status(200).send('OK');
});

استخدم دائمًا سرًا قويًا وقم بتدويره بشكل دوري. لا تكشف عنه أبدًا في كود جانب العميل.

تنفيذ إعادة المحاولة مع التراجع الأسي

يتم تسليم webhooks عبر الإنترنت، لذا تحدث حالات فشل. قد يكون خادمك متوقفًا، أو قد تحدث مشكلة في الشبكة. نظام webhook القوي يعيد محاولة التسليمات الفاشلة مع التراجع الأسي.

عادةً، يعيد المرسل المحاولة إذا لم يتلق استجابة 2xx خلال مهلة زمنية (مثل 5 ثوانٍ). قد يكون جدول إعادة المحاولة: فوري، ثم بعد دقيقة واحدة، 5 دقائق، 30 دقيقة، ساعتين، إلخ، حتى الحد الأقصى لعدد المحاولات (مثل 5).

بصفتك المستقبل، يجب عليك الرد بسرعة لتجنب المهلات. أقر بالاستلام مع 200 OK وعالج الحدث بشكل غير متزامن.

معالجة Idempotency

نظرًا لأن إعادة المحاولة يمكن أن تسبب تسليمات مكررة، يجب أن تكون معالجة الأحداث الخاصة بك idempotent. ضمّن معرف حدث فريد في الحمولة وقم بتخزين المعرفات المعالجة. قبل المعالجة، تحقق مما إذا كان المعرف قد تم التعامل معه بالفعل.

async function processEvent(event) {
  const { id, type, data } = event;
  if (await db.processedEvents.findOne({ id })) {
    return; // Already processed
  }
  await db.processedEvents.insertOne({ id });
  // Handle event based on type
}

استخدم معاملة قاعدة بيانات لضمان وضع علامة على الحدث كمعالج فقط بعد المعالجة الناجحة.

أفضل الممارسات لأمان Webhook

مقارنة: الاستعلام المتكرر مقابل Webhooks

الجانبالاستعلام المتكررWebhooks
زمن الاستجابةمرتفع (على أساس الفاصل الزمني)منخفض (شبه فوري)
حمل الخادممرتفع (طلبات مستمرة)منخفض (فقط عند الأحداث)
التعقيدبسيط في التنفيذيتطلب الأمان وإعادة المحاولة
الموثوقيةتعتمد على تكرار الاستعلامتعتمد على منطق إعادة المحاولة

الأسئلة الشائعة

كيف أختبر webhooks محليًا؟

استخدم أداة مثل ngrok لكشف خادمك المحلي للإنترنت. قم بتكوين مزود webhook لإرسال الأحداث إلى عنوان URL الخاص بـ ngrok.

ماذا لو كان خادمي متوقفًا عند إرسال webhook؟

يجب على المرسل إعادة المحاولة مع التراجع الأسي. تأكد من أن خادمك متاح بشكل كبير ويستجيب بسرعة لتجنب المهلات.

هل يمكنني استخدام توقيعات غير متماثلة بدلاً من HMAC؟

نعم، يستخدم بعض المزودين توقيعات RSA أو ECDSA. تتحقق باستخدام مفتاحهم العام. HMAC أبسط ولكنه يتطلب سرًا مشتركًا.

عند تصحيح أخطاء حمولات webhook، من المفيد تنسيق JSON لتحسين القراءة. جرب JSON Formatter الخاص بنا لطباعة وتنسيق حمولات webhook والتحقق منها فورًا.