Skip to content
جميع المقالات

التحقق من توقيعات Webhook والتعامل مع عمليات إعادة المحاولة بشكل صحيح

تشارلز أرشيبونغ

, المؤسس المشارك

تحقق من توقيع V2 على بايتات الطلب الخام، وارفض الطوابع الزمنية خارج نافذة إعادة التشغيل، وسجل معرف الحدث ضمن قيد فريد قبل القيام بأي عمل، وأعد 2xx فقط بمجرد تخزين الحدث بشكل دائم.

عنوان "توقيعات Webhook وإعادة المحاولات" بجانب رسم توضيحي لنافذة رمزية مع أقواس زاوية، على تدرج لوني نيلي داكن.

أهم النقاط

  • احسب رمز HMAC على بايتات الطلب الخام، وليس على JSON المعاد تسلسله.
  • إن الطابع الزمني الموقع بالإضافة إلى نافذة إعادة التشغيل هو ما يمنع إعادة تشغيل عملية تسليم قديمة وموقعة بشكل صحيح.
  • قم بإزالة التكرارات بناءً على معرف الحدث، وليس معرف التسليم: إعادة الإرسال اليدوي هي تسليم جديد لنفس الحدث.
  • قم بإرجاع 2xx فقط بعد تخزين الحدث بشكل دائم، وقم بالعمل البطيء بعد ذلك.

يستقبل مُستقبِل Webhook ثلاث مهام، وتُنفَّذ هذه المهام بترتيب مُحدَّد. أثبت أنَّ الطلب جاء من Toden عن طريق التحقق من التوقيع على البايتات المستلمة بالضبط. أثبت أنها حديثة بمقارنة الطابع الزمني المُوقّع مع نافذة إعادة التشغيل. ثم أثبت أنك لم تتخذ إجراءً بشأنها مسبقًا بتسجيل مُعرّف الحدث ضمن قيد فريد. بعد هذه الخطوات الثلاث فقط، تُعيد الاتصال.2xxوبعد ذلك فقط تقوم بالعمل الحقيقي.

إذا أخطأت في ترتيب البيانات، فلن يتم التعامل مع أي فشل. وإذا قمت بتحليل البيانات قبل التحقق منها، فسيصل نص مزيف إلى منطق عملك. وإذا تجاهلت الطابع الزمني، فقد يُعاد تشغيل الطلب الملتقط إلى أجل غير مسمى. وإذا قمت بإزالة البيانات المكررة باستخدام مفتاح خاطئ، فستؤدي إعادة إرسال الطلب إلى تسجيل العميل مرتين. تشرح هذه المقالة كل خطوة من هذه الخطوات بالتفصيل. Toden روابط الويب (يفتح في علامة تبويب جديدة) ، مع جهاز استقبال يمكنك تعديله.

ما الذي يصل مع كل شحنة؟

كل عملية توصيل هي HTTP POSTمع JSON نص الطلب ومجموعة من العناوين. العناوين المهمة للتحقق، كما هو موثق في قسم مصادقة الويب هوك (يفتح في علامة تبويب جديدة) :

رأس الصفحة

مثال

ماذا نفعل به؟

X-Myaza-Signature-V2

t=1786441200,v1=8df1…

قم بتحليل الطابع الزمني وكلv1القيمة. تحقق من هذه القيم.

X-Myaza-Replay-Window

300

نافذة إعادة تشغيل نقطة النهاية بالثواني. غير موقّعة، لذا لا تدعها توسّع نطاق فحصك.

X-Myaza-Event-Id

evt_…

مفتاح ثابت لإزالة البيانات المكررة.

X-Myaza-Delivery

del_…

سجل التسليم. مفيد للتتبع، وليس لإزالة البيانات المكررة.

X-Myaza-Event

verification.completed

لأغراض التوجيه. يبقى النص الموقّع هو المرجع المعتمد.

X-Myaza-Signature

sha256=…

توقيع التوافق مع الأنظمة القديمة على الهيكل فقط.

إذا بنيت ضد الأقدمX-Myaza-Signatureلا يزال رأس الطلب يعمل. فهو يوقع على نص الطلب بدون طابع زمني، مما يعني أنه لا يمكنه تحديد ما إذا كان الطلب حديثًا أم لا. انتقل إلى رأس الطلب الإصدار الثاني (V2).

الخطوة 1: التحقق من التوقيع على البايتات الخام

التوقيع هو سداسي عشري صغير HMAC - SHA256 ، باستخدام مفتاح السر الخاص بنقطة النهاية، عبر السلسلة<t>.<rawBody>: الطابع الزمني من رأس الطلب، ثم نقطة، ثم نص الطلب كما وصل تمامًا.

عبارة "تمامًا كما وصلت" هي الجزء الذي يُسبب مشاكل التكامل. معظم أطر عمل الويب تُحلل JSON قبل تشغيل معالجك. إذا قمت بالتحقق من ذلك.JSON.stringify(req.body)أنت بصدد توقيع عملية إعادة تسلسل: قد تختلف المسافات البيضاء وترتيب المفاتيح وتنسيق الأرقام عما تم توقيعه، وقد يفشل التحقق بشكل متقطع بطرق توحي بوجود سر ضعيف. لذا، قم بتهيئة المسار لتسليمك المخزن المؤقت الخام.

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

الخطوة الثانية: رفض الطوابع الزمنية القديمة

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

ارفض أي عملية تسليم يكون طابعها الزمني مفقودًا، أو غير قابل للتحليل، أو أقدم من نافذة إعادة التشغيل، أو في المستقبل البعيد بشكل غير معقول.X-Myaza-Replay-Windowيُخبرك هذا العنوان بنافذة نقطة النهاية المُهيأة، لكنه يقع خارج السلسلة المُوقّعة، لذا يُمكن لأي شخص يُعيد إرسال الطلب تغييره. استخدمه لتضييق نطاق التحقق إذا رغبت، ولا تستخدمه أبدًا لتوسيعه: حدده عند النافذة التي قمتَ بتهيئتها لنقطة النهاية. تأكد من مزامنة ساعة الخادم؛ لأن انحراف الساعة يُؤدي إلى رفض عشوائي لهذا التحقق.

يعود401في حالة وجود توقيع مفقود أو قديم أو غير صالح. لا تقم بإرجاعه.200لإيقاف عمليات إعادة الإرسال: هذا يُعلم المرسل بأنه تم قبول الحدث.

الخطوة 3: إزالة التكرارات في الحدث، وليس في عملية التسليم

يتم إرسال إشعارات الويب مرة واحدة على الأقل. سيؤدي انتهاء مهلة المُستقبِل، أو حدوث خلل في الشبكة بعد ردك، أو قيام أحد أعضاء الفريق بالضغط على "إعادة الإرسال" في لوحة التحكم، إلى عرض نفس الحدث أمامك مرة أخرى.

السؤال هو: أيّ مُعرّف يعني "الشيء نفسه"؟ هناك مُرشّحان، وهما يتصرفان بشكل مختلف:

  • معرّف الحدث (X-Myaza-Event-Id، وidيُحدد العنصر الموجود في المغلف الحدث التجاري. ويبقى هذا العنصر كما هو سواءً في عمليات إعادة الإرسال التلقائية أو اليدوية.

  • رقم تعريف التسليم (X-Myaza-Deliveryيُحدد هذا سجل تسليم واحد. إعادة الإرسال اليدوية تُنشئ سجل تسليم آخر، وبالتالي يحصل على مُعرّف تسليم جديد.

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

insert into received_myaza_events (event_id, event_type, payload)
values (:id, :type, :payload)
on conflict (event_id) do nothing;

هناك حدث واحد يحتاج إلى عناية.verification.status_updatedيتم إرسالها مسطحة، وليست مغلفة.{ id, event, createdAt, data }تستخدم الأحداث الأخرى غلافًا، لذلك لا يحتوي الجسم علىidالحقل. إن أخذ معرف الحدث من العنوان يعمل مع كل نوع من أنواع الأحداث، وهي القاعدة الأبسط.

الخطوة الرابعة: الاعتراف السريع، ثم إنجاز العمل

يعود2xxبمجرد تخزين الحدث بشكل دائم، وليس قبل ذلك. ثم قم بمعالجته خارج نطاق الطلب: تحديث سجل العميل، إخطار مقدم الطلب، فتح تذكرة دعم داخلي. يؤدي بطء المعالجة داخل الطلب إلى حدوث مهلة زمنية، وتُعتبر المهلة الزمنية بمثابة فشل، ويتم إعادة المحاولة.

يجب تخزين أنواع الأحداث غير المعروفة أو تجاهلها مع التأكيد عليها. تُضاف أحداث جديدة إلى الكتالوج بمرور الوقت؛ ورفض حدث لا تتعرف عليه يُؤدي إلى حلقة إعادة محاولة لشيء لم تكن تنوي التعامل معه أصلًا.

جهاز استقبال يقوم بكل شيء.

مقتبس من مثال Node.js في الوثائق، مع تحديد حجم نافذة إعادة التشغيل وإضافة خاصية الإدراج لإزالة التكرار.saveEventOnceوqueueقم بدورك كمخزن ومندوب توصيل مهام.

import crypto from "node:crypto";
import express from "express";

const app = express();

app.post("/webhooks/myaza", express.raw({ type: "application/json" }), async (req, res) => {
  const parts = String(req.header("X-Myaza-Signature-V2") || "").split(",");
  const timestamp = parts.find((p) => p.startsWith("t="))?.slice(2);
  const candidates = parts.filter((p) => p.startsWith("v1=")).map((p) => p.slice(3));
  const MAX_WINDOW = 300; // the replay window configured for this endpoint
  const windowSeconds = Math.min(Number(req.header("X-Myaza-Replay-Window")) || MAX_WINDOW, MAX_WINDOW);

  if (!timestamp || Math.abs(Date.now() / 1000 - Number(timestamp)) > windowSeconds) {
    return res.status(401).send("Expired signature");
  }

  const expected = crypto
    .createHmac("sha256", process.env.MYAZA_WEBHOOK_SECRET)
    .update(`${timestamp}.`)
    .update(req.body)
    .digest("hex");

  const valid = candidates.some((c) =>
    c.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(c), Buffer.from(expected))
  );
  if (!valid) return res.status(401).send("Invalid signature");

  const eventId = req.header("X-Myaza-Event-Id");
  const payload = JSON.parse(req.body.toString("utf8"));
  const isNew = await saveEventOnce(eventId, req.header("X-Myaza-Event"), payload);

  res.status(200).send("OK");
  if (isNew) queue.add("myaza-event", { eventId });
});

لأن الحدث يُخزن قبل الاستجابة، فإن حدوث عطل بين الاستجابة وqueue.addلا يخسر شيئًا: استخدم أداة مسح صغيرة لالتقاط الأحداث المخزنة التي لم تتم معالجتها. أثناء عملية التدوير، احتفظ بكلا السرين واحسب القيمة المتوقعة لكل منهما؛ ثم ينجح الفحص إذا تطابق أي مرشح مع أي سر.

كيف تعمل عمليات إعادة المحاولة، وماذا تعني لك؟

شحنة لا تستلم2xxأو عند انتهاء المهلة، تتم إعادة المحاولة مع التراجع:

محاولة

تأخيرات بعد المحاولة السابقة

1

30 ثانية

2

5 دقائق

3

30 دقيقة

4

ساعاتين

5

24 ساعة

بعد خمس محاولات فاشلة، تم وضع علامة على التسليمFAILEDويمكن إعادة المحاولة يدويًا من لوحة التحكم. ويترتب على ذلك نتيجتان.

أولاً، يمكن استعادة الخدمة في حال انقطاعها لمدة يوم أو يومين دون فقدان البيانات، شريطة إصلاحها قبل المحاولة الأخيرة والتعامل مع سلسلة المحاولات المتكررة بشكل فعال عند عودة الخدمة. فعّل التنبيهات عند تكرار حالات فشل التسليم لتتمكن من معرفة سبب الانقطاع من خلال نظام المراقبة الخاص بك بدلاً من تلقي معلومات من العميل.

ثانيًا، تُعاد محاولات التسليم بشكل مستقل، لذا قد تصل بترتيب غير صحيح. لنفترض أن سير العمل الخاص بك يُوجّه عملية التحقق للمراجعة، ويُوافق عليها مسؤول الامتثال بعد ساعة: اثنانverification.status_updatedلكل حدث جدول زمني خاص لإعادة المحاولة. إذا فشلت عملية التسليم الأولى ونجحت عند إعادة المحاولة، فقد تتلقىapprovedقبلin_reviewالقاعدة الموثقة هي معاملة الأحدثchangedAtباعتبارها النسخة الحالية، لذا قم بتخزينها وتجاهل أي تحديث أقدم منها. عندما تكون المخاطر عالية، اقرأ الحالة الحالية من API قبل التصرف.

اختبره قبل الإنتاج

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

  1. أرسل حدثًا مُحاكىً وتأكد من أن المُستقبِل يتحقق من توقيع V2 ويعيد2xxبسرعة.

  2. قم بتغيير بايت واحد من الجسم الملتقط وتأكد من ذلك.401.

  3. أعد تشغيل الطلب الملتقط بعد انقضاء الفترة الزمنية المحددة، ثم أكد ذلك.401.

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

  5. إرجاع غير2xxمؤقتًا، وتأكيد جدولة إعادة المحاولة.

  6. قم بتدوير السر وتأكد من قبول كلا التوقيعين خلال فترة السماح.

سيتصرف اللاعب الذي يستقبل جميع التمريرات الست بشكل صحيح في اليوم الذي يحدث فيه خطأ ما، وهو اليوم الوحيد الذي يهم فيه الأمر.

المصادر

تشارلز أرشيبونغ

عن الكاتب

Charles Archibong

المؤسس المشارك

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

  • المطورون

    المفاتيح العامة والسرية: إبقاء بيانات التحقق بعيدة عن العميل

    ضع المفتاح العام في تطبيقك، واحتفظ بالمفتاح السري على خادمك. يمكن للمفتاح العام بدء عملية التحقق وقراءة الحد الأدنى من البيانات، وليس بيانات الهوية، لذا ضع في اعتبارك احتمالية تسريبه، وقلل من تكلفة هذا التسريب.

  • Product Updates

    تغيير مسار التحقق دون إصدار نسخة جديدة من التطبيق

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

ابنِ منتجك.وسنتولى الباقي.

الهوية والامتثال من البداية إلى النهاية، وفق معايير عالمية وبأسعار تناسب المؤسسين.

تحقق من توقيعات Webhook وتعامل مع عمليات إعادة المحاولة · Toden