HMAC — كيف تعرف واجهات الاتصال أنك لا تكذب
يمكن لأي خادم إرسال طلب POST إلى نقطة الاتصال الخاصة بك. تُستخدم علامات HMAC كدليل على أن المرسل هو موثوق، وتحدد كيفية التحقق من صحة المحتوى.
كلما أرسلت سترايب دفعة إلى بطاقة، يُرسل مُذكّر (webhook). وكلما دمجت غِيتي طلب تجميع، يُرسل مُذكّر (webhook). تُرسل هذه المنصات طلبات POST إلى عنوان URL الذي قدمته لهم — لكن الحقيقة غير المريحة هي أن أي شخص آخر يمكنه إرسال طلب POST إلى نفس هذا العنوان.
إذًا، كيف يعلم خادمك أن الطلب جاء من سترايب وليس من مهاجم يُفترض عنوانك للنطاق؟ الجواب هو HMAC — وعندما تفهمه، سترى لماذا يُعتبر هذا الطريقة المعيارية في كل منصات الواجهات المُعتمدة.
المشكلة: يمكن لأي شخص إرسال POST إلى نقطة الوجهة الخاصة بك
نُقطة المُذكّر (webhook) مجرد عنوان URL. فهي متاحة علائياً (لأنها يجب أن تكون كذلك، حتى يُمكن للمرسل الوصول إليها)، وتقبل طلبات POST. لا يوجد ما يمنع مُهاجم من إعداد محتوى مزيف وإرساله إلى نقطة الوجهة الخاصة بك.
تخيل أن مُعالج مُذكّرك يفعل هذا عند استلام حدث:
if event["type"] == "payment.completed":
fulfill_order(event["data"]["order_id"])
يمكن لأحد المهاجمين الذين يعرفون عنوان نقطة الوجهة الخاصة بك إرسال حدث مزيف مع أي رقم طلب يرغبون فيه. payment.completed بدون التحقق، سيعمل خادمك على تأكيد طلبات لم تُدفع أبدًا.
تحتاج إلى طريقة لتأكيد أن المحتوى تم إنشاؤه من قبل شخص يمتلك سرًا تشاركه معك — دون نقل هذا السر في الطلب.
ما هو HMAC؟
HMAC تعني كود تحقق الرسالة القائم على التجزئة. إنه بناء يجمع بين دالة تشفير (عادةً SHA-256) وسر مُشارك لإنتاج توقيع. يثبت هذا التوقيع شيئين:
- أصالة — أن الرسالة تم إنشاؤها من قبل شخص يمتلك المفتاح السري
- الكاملية — أن الرسالة لم تُعدل أثناء النقل
خاصية المفتاح HMAC: لا يمكن إنتاج توقيع صالح دون معرفة السر. ولا يمكن عكس التوقيع لاستعادة السر. إنه دليل مُوجه في الاتجاه.
HMAC مقابل تشفير بسيط
تُستخدم دالة تشفير بسيطة (مثل SHA-256) على المحتوى لحل مشكلة الكمال، لكنها لا تضمن التحقق من الهوية. يمكن لمهاجم يُراقب محتوى صالح أن يُعيد حساب التشفير على محتوى مُعدّل. يُدمج المفتاح السري في كل خطوة من عملية التشفير، لذا لا يمكن إنتاج توقيع مطابق حتى بدون المفتاح، حتى لو عرفت خوارزمية التشفير المُستخدمة.
كيف يعمل HMAC في المُذكّر (webhook)
تتكون العملية من ثلاث خطوات: تبادل المفتاح، التوقيع، والتحقق.
الخطوة 1: تبادل المفتاح (تُحدث مرة واحدة)
عند تكوين مُذكّر مع منصة مثل سترايب أو غِيتي، تُولّد المنصة مفتاح مُذكّر مُذكّر مُذكّر وتعرضه لك مرة واحدة. تُخزن في الخادم (لا تُخزن في كود العميل أو الملفات العامة). هذا هو كل شيء — لا يُنقل المفتاح عبر الشبكة مرة أخرى.
الخطوة 2: يُوّصل المرسل المحتوى
قبل إرسال المُذكّر، تُحسب دالة HMAC على جسم الطلب الخام باستخدام المفتاح المُشارك:
signature = HMAC-SHA256(secret_key, request_body)
يُضاف التوقيع إلى الطلب، عادةً في رأس مثل X-Hub-Signature-256 (غِيتي) أو Stripe-Signature (سترايب). يُنقل جسم المحتوى في الجسم دون تغيير.
الخطوة 3: تتحقق من الاستلام
عندما يُستلم المُذكّر من قبل خادمك، تُعاد حساب HMAC باستخدام جسم الطلب الخام والمفتاح المُخزن، ثم تُقارن النتيجة مع التوقيع في الرأس. إذا كانت متطابقة، فإن المحتوى مُصادق عليه وغير مُعدل. إذا لم تكن متطابقة، تُرفض الطلب.
expected = HMAC-SHA256(your_secret, raw_body)
if not constant_time_equal(expected, header_signature):
return 401
يلاحظ مُقارنة منتظمة الزمن — سنعود لسبب أهميتها.
أمثلة واقعية
Stripe
تُرسل ستريب رأسًا Stripe-Signature رأس يحتوي على توقيع وتوقيت وواحد أو أكثر من التوقيعات:
Stripe-Signature: t=1679000000,v1=abc123...,v0=oldformat...
يُحسب التوقيع على timestamp.payload (مُدمجة مع نقطة). يُسمح لسترايب بحماية ضد هجمات التكرار — إذا سجّل مهاجم طلب مُوّافق ويرسله لاحقًا، يمكن رفضه لأن التوقيت أصبح قديمًا.
جيثب
تُرسل غِيتي رأسًا X-Hub-Signature-256 بصيغة sha256=<hex_digest>. يكون التوقيع HMAC-SHA256 للجسم الخام باستخدام المفتاح المُخصص في إعدادات مخزنك.
Shopify
تستخدم شوبيفي رأسًا X-Shopify-Hmac-Sha256 بتوقيع مُرمّز بـ Base64 — نفس المفهوم، لكن بتمثيل مختلف.
التحقق في الكود
إليك كيف يظهر التحقق عبر ثلاث لغات شائعة. يبقى النمط متماثلًا — فقط تختلف المكالمات في المكتبات.
بايثون
import hmac
import hashlib
def verify_webhook(secret: str, payload: bytes, signature_header: str) -> bool:
expected = hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
received = signature_header.removeprefix("sha256=")
return hmac.compare_digest(expected, received)
نود.جي اس
const crypto = require('crypto');
function verifyWebhook(secret, rawBody, signatureHeader) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
const received = signatureHeader.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(received)
);
}
PHP
function verifyWebhook(string $secret, string $rawBody, string $signatureHeader): bool {
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);
return hash_equals($expected, $signatureHeader);
}
أخطاء تُهدد أمان النظام
1. استخدام == بدلًا من مقارنة منتظمة الزمن
المقارنة المعيارية للسلسلة (==, ===) تُختصر فورًا عند اكتشاف عدم التوافق. هذا يخلق قناة زمنية جانبية : يمكن للاختراق قياس مدة استجابة خادمك عند رفض توقيعات مختلفة. تأخذ السلاسل التي تُشارك مقدارًا أطول من المقدار وقتًا أطول لرفضها. مع عدد كبير من الطلبات، يمكن للهجمة استرداد التوقيع بحروف واحدة بواحدة.: يمكن للاختراق أن يقيس مدة وقت رفض خادمك لتوقيعات مختلفة. تأخذ السلاسل التي تمتلك مقدارًا أطول من المقدمة وقتًا أطول لرفضها. مع عدد كبير من الطلبات، يمكن للاختراق استخدام هذا لاستعادة توقيع صالح حرفًا بعد حرف.
استخدم دائمًا مقارنة منتظمة الزمن: hmac.compare_digest() في بايثون، crypto.timingSafeEqual() في نود جي، hash_equals() في بَيْثو.
2. تحليل الجسم قبل التحقق
يُحسب HMAC على البُعد الخام لطلب الجسم. إذا قمت بتحليل JSON أولاً ثم إعادة تسلسله للتحقق، فقد تُحصل على تسلسل بُعد مختلف (ترتيب مفتاح مختلف، فراغ، تشفير). يجب دائمًا التقاط الجسم الخام قبل أن يُعالجها مُعالج الجسم في إطارك، ثم التحقق من ذلك.
3. عدم التحقق من هجمات التكرار
الطلب المُوّافق المُوقّع مُعتبر مُتاحًا إلى الأبد — ما لم تتحقق من التوقيت. إذا كانت المنصة تُدرج توقيتًا في نمط التوقيع (مثل سترايب، لكن غِيتي لا تفعل ذلك)، رفض الطلبات التي يكون فيها التوقيت قديمًا أكثر من بضع دقائق. هذا يمنع مهاجم من سجّل طلبًا قانونيًا ثم إعادة إرساله.
4. تثبيت المفتاح في كود المصدر
يجب أن يُخزن مفتاح المُذكّر في متغيرات بيئة أو مدير السر، وليس في نظام التحكم في الإصدارات. إذا تسرب المفتاح، يمكن للاختراق إنشاء أي محتوى إلى الأبد — حتى تُعاد تثبيت المفتاح.
ما لا يحمي HMAC من التهديدات
يُثبت HMAC أن المحتوى تم التوقيع عليه من قبل شخص يمتلك المفتاح. لكنه لا يحمي من: لا الحماية من:
- مُهاجم يُسيطر على المرسل — إذا تم تدمير بنية التوقيع في سترايب، فإن الطلب المزيف سيكون له توقيع صالح
- هجمات التكرار — ما لم تتحقق من التوقيت أو المفتاح المُستخدم
- السرية — HMAC لا يُشفّر أي شيء؛ ينتقل المحتوى في صورة نصية (رغم أن HTTPS يُعالج ذلك)
لأغلب تكاملات المُذكّر، يكفي HMAC مع HTTPS.
تثبيت ملحقاتنا
أضف أدوات IO إلى متصفحك المفضل للوصول الفوري والبحث بشكل أسرع
恵 وصلت لوحة النتائج!
لوحة النتائج هي طريقة ممتعة لتتبع ألعابك، يتم تخزين جميع البيانات في متصفحك. المزيد من الميزات قريبا!
