شرح Webhooks: الحمولات الموقعة وإعادة المحاولة
لماذا تحتاج Webhooks إلى حمولات موقعة وإعادة محاولة
لقد قمت للتو بدمج خدمة طرف ثالث ترسل webhooks إلى نقطة النهاية الخاصة بك. كل شيء يعمل في الاختبار، ولكن في الإنتاج تلاحظ أحداثًا مفقودة، وتسليمات مكررة، والأسوأ من ذلك — شخص ما ينتحل الطلبات إلى عنوان URL الخاص بـ webhook. بدون تدابير أمان وموثوقية مناسبة، يمكن أن تصبح webhooks مصدرًا للأخطاء والثغرات الأمنية.
Webhooks هي عمليات استدعاء HTTP تُعلم تطبيقك عند حدوث حدث. إنها بسيطة من حيث المفهوم ولكنها صعبة في الممارسة لأنها تعبر حدود الثقة ومشكلات موثوقية الشبكة. في هذا الدليل، سنغطي جانبين حاسمين: الحمولات الموقعة للتحقق من الأصالة وإعادة المحاولة لضمان التسليم. سنستعرض تطبيقات عملية يمكنك تطبيقها على الفور.
فهم الحمولات الموقعة
الحمولة الموقعة هي طريقة للمرسل لإثبات أن webhook جاء منه ولم يتم العبث به. يقوم المرسل بحساب توقيع تشفيري على جسم الطلب (وأحيانًا الرؤوس) باستخدام سر مشترك، ثم يضمّن هذا التوقيع في رأس. تعيد نقطة النهاية الخاصة بك حساب التوقيع وتقارنه.
الخوارزمية الأكثر شيوعًا هي HMAC-SHA256. إنها سريعة وآمنة ومدعومة على نطاق واسع. إليك كيفية عملها:
- يشترك المرسل والمستقبل في مفتاح سري (يتم تكوينه في إعدادات webhook).
- لكل webhook، ينشئ المرسل توقيعًا عن طريق تجزئة الحمولة مع السر باستخدام HMAC-SHA256.
- يتم إرسال التوقيع في رأس مثل
X-Signature. - يحسب المستقبل نفس HMAC ويقارنه باستخدام مقارنة ثابتة الوقت لمنع هجمات التوقيت.
تنفيذ التحقق من HMAC في Node.js
لنرى مثالًا ملموسًا. لنفترض أن المرسل يضمّن التوقيع في X-Signature كسلسلة سداسية عشرية. إليك كيفية التحقق منه في مسار Express:
const crypto = require('crypto');
const express = require('express');
const app = express();
// Use raw body for signature verification
app.use('/webhook', express.raw({ type: 'application/json' }));
app.post('/webhook', (req, res) => {
const signature = req.headers['x-signature'];
const secret = process.env.WEBHOOK_SECRET;
const expected = crypto
.createHmac('sha256', secret)
.update(req.body)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).send('Invalid signature');
}
// Process the event
const event = JSON.parse(req.body);
console.log('Received event:', event.type);
res.status(200).send('OK');
});
app.listen(3000);
هام: استخدم دائمًا جسم الطلب الخام لحساب التوقيع، وليس JSON المُحلل. يمكن أن يؤدي التحليل وإعادة التسلسل إلى تغيير المسافات البيضاء والترتيب، مما يكسر التوقيع.
الطابع الزمني والحماية من إعادة التشغيل
التوقيعات وحدها لا تمنع هجمات إعادة التشغيل. يمكن للمهاجم التقاط طلب موقّع صالح وإعادة إرساله لاحقًا. للتخفيف من ذلك، قم بتضمين طابع زمني في الحمولة الموقعة أو في رأس. على سبيل المثال، أضف رأس X-Timestamp مع وقت Unix. يتحقق المستقبل من أن الطابع الزمني ضمن نافذة تسامح (مثل 5 دقائق) ويرفض الطلبات الأقدم.
أيضًا، قم بتضمين الطابع الزمني في حساب التوقيع لمنع العبث. يحسب المرسل HMAC على timestamp + '.' + body. يفعل المستقبل الشيء نفسه ويقارن.
تنفيذ إعادة المحاولة للتسليم الموثوق
تنتقل webhooks عبر الإنترنت العام، لذا فإن الفشل أمر لا مفر منه: مهلات الشبكة، أخطاء الخادم (5xx)، أو تعطل نقطة النهاية الخاصة بك مؤقتًا. يجب أن يعيد نظام webhook القوي محاولة التسليمات الفاشلة.
استراتيجيات إعادة المحاولة
تشمل استراتيجيات إعادة المحاولة الشائعة:
- تأخير ثابت: إعادة المحاولة بعد فترة زمنية ثابتة (مثل دقيقة واحدة). بسيط ولكن يمكن أن يرهق إذا حدثت العديد من حالات الفشل.
- التراجع الأسي: الانتظار لفترة أطول بعد كل محاولة (مثل 1 ثانية، 2 ثانية، 4 ثوانٍ، 8 ثوانٍ...). هذا هو النهج الأكثر شيوعًا والموصى به.
- الاهتزاز: إضافة عشوائية إلى التأخير لتجنب مشاكل القطيع المتدافع عندما تفشل العديد من webhooks في وقت واحد.
قد تكون السياسة النموذجية: إعادة المحاولة حتى 5 مرات مع تراجع أسي واهتزاز، بدءًا من 10 ثوانٍ. بعد ذلك، قم بتمييز التسليم على أنه فاشل وربما تنبيه.
الخصائص التكرارية: التعامل مع التكرارات
يمكن أن تسبب إعادة المحاولة تسليمات مكررة. يجب أن تكون نقطة النهاية الخاصة بك متسقة تكرارياً — معالجة نفس الحدث مرتين يجب ألا تسبب آثارًا جانبية مثل تحصيل رسوم مزدوجة من العميل. قم بتضمين معرف حدث فريد في الحمولة (مثل event.id) وقم بتخزين المعرفات المعالجة لفترة معقولة. قبل المعالجة، تحقق مما إذا كان المعرف قد تم رؤيته.
بدلاً من ذلك، صمم عملياتك لتكون متسقة تكرارياً بشكل طبيعي (مثل استخدام upserts بدلاً من inserts).
الرد بسرعة
يجب أن تؤكد نقطة النهاية الخاصة بـ webhook الاستلام بسرعة (خلال بضع ثوانٍ) وتعالج الحدث بشكل غير متزامن. إذا استغرقت المعالجة وقتًا طويلاً، فقد ينتهي وقت المرسل ويعيد المحاولة، مما يسبب تكرارات. استخدم قائمة انتظار: رد بـ 200 OK فورًا بعد التحقق من التوقيع، ثم ضع الحدث في قائمة الانتظار للمعالجة في الخلفية.
مقارنة تدابير أمان Webhook
| التدبير | الغرض | تعقيد التنفيذ |
|---|---|---|
| توقيع HMAC | التحقق من الأصالة والسلامة | منخفض |
| التحقق من الطابع الزمني | منع هجمات إعادة التشغيل | منخفض |
| مفاتيح التكرار | تجنب المعالجة المكررة | متوسط |
| قائمة IP المسموح بها | تقييد لعناوين IP المرسل المعروفة | منخفض (لكنه هش) |
| HTTPS | تشفير البيانات أثناء النقل | منخفض |
في حين أن قائمة IP المسموح بها يمكن أن تضيف طبقة، إلا أنها ليست موثوقة لأن عناوين IP المرسل قد تتغير. اعتمد بشكل أساسي على التوقيعات.
اختبار نقطة نهاية Webhook الخاصة بك
استخدم أدوات مثل ngrok لكشف الخادم المحلي الخاص بك وتشغيل webhooks اختبارية من لوحة تحكم المزود. تحقق من صحة التحقق من التوقيعات وأن إعادة المحاولة تعمل عن طريق محاكاة حالات الفشل (مثل إرجاع 500 لأول بضع محاولات).
عند التصحيح، قد تحتاج إلى فحص حمولات JSON. يمكن أن تساعدك JSON Formatter في تنسيق والتحقق من حمولات webhook بسرعة.
الأسئلة الشائعة
ما هي حمولة webhook الموقعة؟
تتضمن الحمولة الموقعة توقيعًا تشفيريًا (عادة HMAC) يسمح للمستقبل بالتحقق من أن الطلب جاء من المرسل المتوقع ولم يتم تغييره. يتم حسابه باستخدام سر مشترك.
كم مرة يجب أن أعيد محاولة webhook فاشل؟
لا يوجد رقم عالمي، ولكن 3-5 مرات إعادة محاولة مع تراجع أسي شائعة. بعد ذلك، سجل الفشل وفكر في التنبيه. الهدف هو التعامل مع المشكلات العابرة دون إعادة محاولة لا نهائية.
هل يمكنني استخدام JWT لتوقيعات webhook؟
نعم، بعض المزودين يستخدمون JWT (رموز JSON Web) لتوقيع webhooks. يحتوي الرمز على مطالبات ويتم توقيعه باستخدام سر أو مفتاح خاص. التحقق مشابه: تحقق من صحة التوقيع وتحقق من المطالبات مثل انتهاء الصلاحية.
هل أنت مستعد لتنفيذ webhooks آمنة؟ ابدأ بإنشاء سر قوي، وإضافة التحقق من HMAC، وإعداد إعادة المحاولة مع التكرارية. لفحص سريع للحمولة أثناء التطوير، جرب JSON Formatter الخاص بنا لتنسيق والتحقق من JSON webhook.