BroadNet
HTTP API & webhooksالأمن

التحقّق من توقيع webhook: HMAC-SHA256 والحماية من إعادة التشغيل

آخر تحديث 5 دقائق للقراءةقائم على المعايير
إجابة مباشرة

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

§01

لماذا التواقيع لا قوائم السماح بعناوين IP

وجهة webhook عنوان HTTPS عام، فبإمكان أي أحد أن يُرسل إليها عبر POST. وTLS يُثبِت أن العميل بلغ خادمك؛ لكنه لا يُثبِت البتّة أن الطلب أتى فعلًا من منصّة المراسلة. وقائمة السماح بعناوين IP هشّة (عناوين المرسِلين تتغيّر، وتفشل خلف مخرج مشترك). والجواب القياسي هو رمز مصادقة رسالة مُفتاح: يتشارك المرسِل والمستقبِل سرًّا، فيوقّع المرسِل كل حمولة به، ويتحقّق المستقبِل. وHMAC (‏RFC 2104) مع SHA-256 (‏RFC 6234) هو البناء الشائع لأنه سريع ومُعيَّر — وخلافًا لتجزئة مجرّدة لسرّ مُلحَق بالرسالة — مُثبَتُ المقاومة لهجمات تمديد الطول. والتوقيع الصحيح يُثبِت أمرين معًا: أن الحمولة أنتجها حائزٌ للسرّ، وأنها لم تُبدَّل أثناء النقل.

§02

ترويستا التوقيع والطابع الزمني

يُضمِّن المرسِل ترويستين على الأقل: طابعًا زمنيًا (وقت توقيع الطلب) وتوقيعًا. والمحتوى الموقَّع ليس الـ JSON وحده بل سلسلة قانونية تربط الطابع الزمني بالجسم — عادةً الطابع الزمني وفاصلًا والجسم الخام للطلب — بحيث لا يستطيع مهاجم نقل توقيع صالح إلى حمولة أخرى أو إعادته في وقت مختلف. وتتباين التسمية بحسب المورّد (‏Webhook-Signature / Webhook-Timestamp، أو X-Signature / X-Timestamp)، لكن البناء واحد. والتوقيع عادةً بصيغة hex أو base64 وكثيرًا ما يحمل بادئة مخطّط (مثلًا ‎v1,‎) بحيث يمكن تدوير السرّ إلى مخطّط جديد دون كسر المتحقّقين القدامى.

المحتوى الموقَّع وتخطيط الترويسةtext
signed_content = timestamp + '.' + raw_body
signature      = HMAC_SHA256(secret, signed_content)

Webhook-Timestamp: 1752916522
Webhook-Signature: v1,3f2b8c9a1d...   # hex or base64, scheme-prefixed
§03

التحقّق مقابل الجسم الخام

أشيع خطأ في التحقّق هو توقيع جسم أُعيد تسلسله. فـ HMAC يُحسَب على البايتات بالضبط، لذا على المستقبِل أن يتحقّق مقابل الجسم الخام للطلب تمامًا كما ورد — قبل أي تحليل وإعادة ترميز لـ JSON. فالتحليل إلى كائن ثم إعادة التسلسل يعيد ترتيب المفاتيح، ويغيّر الفراغات، ويُطبِّع الأرقام وهروب Unicode، فينتج بايتات مختلفة وعدم تطابق في التوقيع حتى لو لم يُعبَث بشيء. وعمليًا يعني هذا التقاط الجسم الخام في إطار الويب قبل أن تستهلكه وسائط تحليل الجسم، والتحقّق من HMAC على تلك البايتات، ثم فقط تحليل الـ JSON للاستخدام. والمحرَف مهمّ أيضًا: وقّع وتحقّق من تسلسل الثمانيات نفسه (‏UTF-8)، لا من سلسلة مُفكَّكة الترميز.

التحقّق (شفرة توضيحية) — الجسم الخام، زمن ثابتtext
raw   = request.raw_body            # exact bytes, pre-parse
ts    = header('Webhook-Timestamp')
sig   = header('Webhook-Signature')

if abs(now() - ts) > TOLERANCE: reject   # replay window
expected = hmac_sha256(secret, ts + '.' + raw)
if not constant_time_equal(expected, sig): reject
accept(parse_json(raw))
§04

الحماية من إعادة التشغيل: تسامح الطابع الزمني وقيمة nonce

التوقيع وحده لا يوقف إعادة التشغيل — فطلبٌ ملتقَط لكنه صالح يمكن إعادة إرساله لاحقًا. وآليّتان تسدّان تلك الثغرة. أولًا، لأن الطابع الزمني داخل المحتوى الموقَّع، يقارنه المستقبِل بساعته الخاصة ويرفض أي شيء أقدم من تسامح (بضع دقائق عادةً، واسع بما يكفي لاستيعاب انحراف الساعة وتأخّر إعادة الإرسال، وضيّق بما يكفي لتقييد نافذة إعادة التشغيل). ثانيًا، للضمانات الأصرم يُسجّل المستقبِل قيمة nonce — معرّف الحدث الفريد — لمدّة التسامح ويرفض طلبًا ثانيًا يحمل معرّفًا رآه من قبل. فتسامح الطابع الزمني يحدّ النافذة؛ ومخزن الـ nonce يجعل إعادة التشغيل داخل تلك النافذة عديمة الأثر. ولاحظ أن هذا مكمّل لتكافؤ المستهلِك الذي يعالج التكرار المشروع لا إعادات التشغيل الخبيثة.

ضوابط الحماية من إعادة التشغيل
التحكميدافع ضدالضبط
الطابع الزمني في المحتوى الموقّعنقل توقيع إلى توقيت جديديجب أن يكون موقّعًا، لا مجرد ترويسة
نافذة التسامح مع الطابع الزمنيإعادة تشغيل متأخرة لطلب تم التقاطهبضع دقائق؛ موازنة بين انحراف الساعة والتعرّض
ذاكرة تخزين مؤقت لـ Nonce / event-idإعادة إرسال (replay) ضمن نافذة التسامحخزّن المعرّفات مؤقتًا لمدة لا تقل عن هامش التسامح
مقارنة ثابتة الوقتقناة جانبية توقيتية على التوقيعدائماً، بغض النظر عما سبق
§05

المقارنة ثابتة الزمن

مقارنة التوقيع المحسوب والمستلَم بفحص مساواة اعتيادي يُسرِّب معلومات: فالمقارنة بايتًا بايتًا تعود أسرع عند اختلاف مبكّر، والمهاجم القادر على قياس ذلك التوقيت يستطيع استرداد التوقيع الصحيح بايتًا في المرة. لذا يجب أن يستخدم التحقّق مقارنة ثابتة الزمن (مستقلّة عن الطول) — دالّة المقارنة الآمنة زمنيًا في كل منصّة — تفحص دائمًا الطول الكامل قبل أن تعود. وهذا رخيص وغير قابل للتفاوض؛ وهو الفرق بين مخطّط توقيع سليم وآخر يمكن هزمه بعدد كافٍ من الطلبات المُوقَّتة.

§06

تدوير السرّ

يجب أن تكون أسرار التوقيع قابلة للتدوير دون إسقاط webhooks مشروعة في منتصف الطريق. والنمط الآمن هو مفاتيح متداخلة: يُزوَّد المرسِل بسرّ جديد بينما يبقى القديم صالحًا، وأثناء التداخل إمّا يوقّع المرسِل بكليهما (فيُصدِر توقيعين) أو يقبل المستقبِل توقيعًا يطابق أيًّا من السرّين. وبمجرّد تأكّد كل منتِج على السرّ الجديد، يُتقاعَد القديم. وبادئة المخطّط على التوقيع (‎v1, v2,‎) تجعل هذا مرئيًا وتتيح لتغيير خوارزمي مُصدَّر أن يرافقها. وأداة التحقّق من توقيع webhook المستضافة لدى BroadNet تتيح لك فحص حمولة وطابع زمني وسرّ مقابل HMAC متوقَّع دون كتابة شفرة؛ وميكانيكا توقيع DLR بـ HMAC-SHA256 مغطّاة في مكتبة الأجوبة.

التعليمات

الأسئلة المتكررة

كيف يُحسَب توقيع webhook؟

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

لماذا يجب أن أتحقّق مقابل الجسم الخام للطلب؟

لأن HMAC يُحسَب على البايتات بالضبط. فإن حلّلت الـ JSON وأعدت تسلسله، تغيّر ترتيب المفاتيح والفراغات وتنسيق الأرقام وهروب Unicode، فتختلف البايتات ويفشل تطابق التوقيع حتى لو لم يُعبَث بشيء. التقط الجسم الخام قبل وسائط تحليل الجسم، ثم تحقّق، ثم حلّل.

كيف يوقف تسامح الطابع الزمني هجمات إعادة التشغيل؟

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

لماذا تُشترَط المقارنة ثابتة الزمن؟

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

كيف أُدوّر سرّ توقيع webhook بأمان؟

استخدم مفاتيح متداخلة. زوّد السرّ الجديد بينما يبقى القديم صالحًا؛ وأثناء التداخل يوقّع المرسِل بكليهما أو يقبل المستقبِل أيًّا منهما، فلا يُسقَط أي webhook مشروع. وبمجرّد تأكّد كل المنتِجين على السرّ الجديد، تقاعَد القديم. وبادئة المخطّط على التوقيع تجعل الانتقال مرئيًا.

أليس HTTPS كافيًا للوثوق بـ webhook؟

لا. TLS يُثبِت أن النقل مُشفَّر وأن العميل بلغ خادمك، لكن ليس أن الطلب أتى من منصّة المراسلة — فأي أحد يستطيع الإرسال عبر POST إلى عنوان عام. ووحده توقيع HMAC بسرّ مشترك يصادق المرسِل ويُثبِت أن الحمولة لم تُعدَّل. وقائمة السماح بعناوين IP بديل ضعيف لأن عناوين المخرج تتغيّر.

مراجع

مواصفات المصدر

تستند هذه الصفحة إلى المعايير المنشورة. يفتح في علامة تبويب جديدة.

BNتتم صيانته بواسطة فريق الهندسة BroadNet·خدمة الرسائل عالية الجودة منذ عام 2003
ضعه على السلك

قم بالبناء باستخدام ربط SMPP حقيقي وواجهة برمجة تطبيقات REST.

اطلب الوصول التجريبي باستخدام مفاتيح اختبار حقيقية: أرسل أولاً إلى رقمك الذي تم التحقق منه، ثم انتقل إلى الإنتاج عبر أكثر من 200 دولة بمجرد الموافقة على معرّف المُرسِل ومساراته.