في جملة واحدة
HMAC هو مصافحة تشفيرية تستخدم مفتاحًا سريًا مشتركًا لإثبات أن الرسالة أصلية ولم يتم العبث بها.
المشكلة التي يحلها
في الأيام الأولى للإنترنت، أيام الغرب المتوحش، كان إرسال رسالة أشبه بإرسال بطاقة بريدية. أي شخص يعترضها يمكنه قراءتها، وربما حتى الخربشة عليها قبل إرسالها. إذا تلقيت بطاقة بريدية تقول "قابلني في منتصف الليل، وأحضر النقود"، كيف يمكنك التأكد من أنها حقًا من عميلك السري، وليس من عدوته اللدودة، "إيف الشريرة"؟ وكيف يمكنك التأكد من أن النص الأصلي لم يكن "قابلني عند الظهيرة لتناول غداء ودي"؟
هذه هي المشكلة المزدوجة المتمثلة في الموثوقية (هل هي حقًا منك؟) والسلامة (هل تم تغييرها؟).
قد تبدو دالة هاش تشفيرية بسيطة (مثل SHA-256) خطوة أولى جيدة. يمكنك عمل هاش لرسالتك، وإرسال الرسالة والهاش معًا، ويمكن للمستقبل إعادة حساب الهاش للرسالة ليرى ما إذا كانا متطابقين. رائع! هذا يحل مشكلة السلامة. إذا تم تغيير بايت واحد فقط من الرسالة، فلن يتطابق الهاش.
لكن هذا لا يحل مشكلة الموثوقية. يمكن لـ "إيف الشريرة" ببساطة تغيير الرسالة، وحساب هاش جديد لرسالتها الجديدة، وإرسالهما معًا. سيرى المستقبل أن الهاش يطابق الرسالة، لكن ليس لديه طريقة لمعرفة أن الحزمة بأكملها مزورة.
هنا يأتي دور HMAC (كود مصادقة الرسائل المعتمد على الهاش). من خلال إدخال مفتاح سري مشترك في عملية الهاش، يُنشئ HMAC توقيعًا لا يمكن إنتاجه إلا من قبل شخص يمتلك هذا المفتاح السري. إنه الفرق بين ختم الشمع البسيط (يمكن لأي شخص صنعه) وختم الشمع المصنوع بخاتم توقيع فريد (لا يملكه إلا الملك). يمنحنا HMAC كلًا من السلامة والموثوقية.
كيف يعمل تحت الغطاء
HMAC ليس نوعًا جديدًا من دوال الهاش؛ بل هو وصفة تستخدم دوال الهاش الموجودة (مثل SHA-256) بطريقة ذكية ومحددة. المواصفات الرسمية هي RFC 2104، لكن دعنا نبسطها بلغة سهلة.
المكونات
لتحضير HMAC، تحتاج إلى ثلاثة أشياء:
- الرسالة (Message): البيانات التي تريد حمايتها. يمكن أن تكون حمولة JSON لـ webhook، أو سلسلة من معاملات URL، أو أي قطعة من البيانات الثنائية (binary data).
- المفتاح السري (Secret Key): سلسلة من البايتات (bytes) لا يعرفها سوى المرسل والمستقبل. هذا هو المكون السحري. إذا تم كشفه، ينهار النظام بأكمله.
- دالة الهاش (Hash Function): خوارزمية قياسية مثل SHA-1 أو SHA-256 أو SHA-512. اختيار دالة الهاش يحدد طول توقيع HMAC النهائي (على سبيل المثال، HMAC-SHA256 ينتج توقيعًا بطول 256 بت).
الوصفة (خوارزمية HMAC)
قد تعتقد أنه يمكنك ببساطة تنفيذ hash(key + message). يبدو الأمر بسيطًا، لكن هذا البناء عرضة لبعض الخدع التشفيرية الذكية التي تسمى "هجمات تمديد الطول" (length extension attacks). بناء HMAC الرسمي أكثر تعقيدًا بقليل، خصيصًا لمنع هذه الهجمات. إنه يستخدم عملية هاش مزدوجة.
إليك نظرة مبسطة على الخطوات:
تحضير المفتاح: تعمل دالة الهاش على كتل بيانات ذات حجم ثابت (على سبيل المثال، 64 بايت لـ SHA-256). يجب تحضير المفتاح ليلائم حجم هذه الكتلة.
- إذا كان المفتاح أطول من حجم الكتلة، تقوم بعمل هاش للمفتاح نفسه وتستخدم تلك النتيجة كمفتاح جديد.
- إذا كان المفتاح أقصر من حجم الكتلة، فإنك تملؤه بالبايتات الصفرية (padding) حتى يصل إلى حجم الكتلة.
إنشاء المفاتيح الداخلية والخارجية: من هذا المفتاح المحضّر، نشتق مفتاحين منفصلين.
ipad(الحشوة الداخلية): بايت ثابت (0x36) مكرر لملء حجم الكتلة.opad(الحشوة الخارجية): بايت ثابت مختلف (0x5C) مكرر لملء حجم الكتلة.
نقوم بإنشاء
inner_padded_keyبأخذ مفتاحنا المحضّر وعمل عملية XOR له معipad. ونقوم بإنشاءouter_padded_keyبعمل XOR للمفتاح المحضّر معopad.تنفيذ الهاش المزدوج: الآن نأتي للحدث الرئيسي.
- الهاش الداخلي: دمج
inner_padded_keyمع الرسالة الأصلية، وتشغيلها عبر دالة الهاش. - الهاش الخارجي: دمج
outer_padded_keyمع نتيجة الهاش الداخلي، وتشغيل ذلك عبر دالة الهاش.
- الهاش الداخلي: دمج
النتيجة النهائية لهذا الهاش الخارجي هي توقيع HMAC الخاص بك!
في الكود الزائف (pseudocode)، يبدو الأمر هكذا:
function hmac(key, message, hash_function, block_size) {
// 1. Prepare the key
if (key.length > block_size) {
key = hash_function(key);
}
if (key.length < block_size) {
key = pad_with_zeros(key, block_size);
}
// 2. Create inner and outer padded keys
o_key_pad = key XOR (0x5C repeated to block_size);
i_key_pad = key XOR (0x36 repeated to block_size);
// 3. Perform the double hash
inner_hash_result = hash_function(i_key_pad + message);
final_hmac = hash_function(o_key_pad + inner_hash_result);
return final_hmac;
}
لماذا الهاش المزدوج؟
هذه البنية "الداخلية ثم الخارجية" هي الخلطة السرية. الهاش الداخلي يجمع بين السر والرسالة. ثم يقوم الهاش الخارجي بشكل أساسي بعمل هاش لـ نتيجة العملية الأولى مرة أخرى مع السر. هذا "يختم" الهاش الداخلي. إنه يجعل من المستحيل حسابيًا على المهاجم التلاعب بنتيجة الهاش المتوسطة دون معرفة المفتاح، وبالتالي إحباط هجمات تمديد الطول والاختراقات التشفيرية المحتملة الأخرى. إنه بناء قوي ومُثبت صمد أمام اختبار الزمن.
قصص من الواقع
حارس الـ Webhook في GitHub
كان لدى فريق في شركة ناشئة خادم تكامل مستمر (CI) مُعد لنشر تطبيقهم تلقائيًا إلى بيئة الإنتاج في كل مرة يدفع فيها أحدهم إلى فرع main. كان المحفز هو webhook من GitHub: طلب POST يُرسل من خوادم GitHub إلى URL عام على خادم CI الخاص بهم. في إحدى الليالي، عثر متدربهم المازح على الـ URL العام، وباستخدام أمر cURL بسيط، بدأ في إرسال حمولات webhook مزيفة، مما أدى إلى العشرات من عمليات النشر عديمة الفائدة والمستهلكة للموارد.
أصلحت المطورة المسؤولة الأمر في 15 دقيقة. في إعدادات الـ webhook على GitHub، أنشأت "سرًا" طويلاً وعشوائيًا. نسخت هذا السر وقامت بتعيينه كمتغير بيئة على خادم CI الخاص بهم. أصبح GitHub الآن يستخدم هذا السر لإنشاء توقيع HMAC-SHA256 لكل حمولة webhook، وإرساله في هيدر X-Hub-Signature-256. تم تحديث كود خادم CI ليقوم بنفس حساب HMAC على نص الطلب الخام الذي يتلقاه، باستخدام نفس السر. إذا تطابق توقيعه المحسوب مع التوقيع الموجود في الهيدر، تتم معالجة الطلب. وإذا لم يتطابق، يتم رفضه مع خطأ 403 Forbidden. توقفت المقالب على الفور.
الدرس المستفاد: أمّن دائمًا الـ webhooks الخاصة بك عن طريق التحقق من توقيع HMAC. لا تثق بأي طلب وارد حتى يتم التحقق من موثوقيته.
تأمين جرة كوكيز الـ API
كان مطور يبني خدمة تستخدم كوكي (cookie) بسيطة وموقعة للمصادقة. عند تسجيل دخول المستخدم، يصدر الخادم كوكي تحتوي على user_id الخاص به وطابع زمني expiry. لمنع المستخدمين من تعديل الكوكي الخاصة بهم ليصبحوا مستخدمًا آخر (على سبيل المثال، تغيير user_id=123 إلى user_id=1)، أضاف المطور توقيع HMAC.
كانت حمولة الكوكي تبدو كالتالي: user_id=123&expiry=1678886400. يقوم الخادم بتوقيع هذه السلسلة النصية بالضبط باستخدام مفتاح سري مخزن على الخادم. الكوكي النهائية التي أُرسلت إلى المتصفح كانت data="user_id=123&expiry=1678886400"&signature="sha1=2a8b...".
عندما يقوم المستخدم بطلب لاحق، يعيد متصفحه إرسال الكوكي. يأخذ الخادم جزء data، ويعيد حساب توقيع HMAC باستخدام مفتاحه السري، ويقارنه بجزء signature من الكوكي. إذا تطابقا، يعرف الخادم أن معرف المستخدم وتاريخ انتهاء الصلاحية شرعيان ولم يتم العبث بهما.
الدرس المستفاد: HMAC طريقة رائعة لإنشاء توكنز (tokens) أو كوكيز (cookies) "عديمة الحالة" (stateless) ومقاومة للعبث، وتشكل أساسًا للعديد من أنظمة المصادقة، بما في ذلك JSON Web Tokens (JWTs).
التحويل البنكي الذي لم يتم اختطافه
قامت منصة تجارة إلكترونية بالتكامل مع API لمعالج دفعات لبدء عمليات الدفع لمورديها. كانت استدعاءات الـ API عبارة عن رسالة JSON بسيطة: {"vendor_id": "ven_abc", "amount": 500.00, "currency": "USD"}. كانت المنصة قلقة بشأن هجوم "رجل في المنتصف" (MITM). حتى عبر HTTPS الذي يشفر حركة المرور، يمكن لمهاجم متطور (في بعض السيناريوهات النظرية، كما هو الحال مع هيئة إصدار شهادات مخترقة) اعتراض الطلب وتعديله. يمكنه تغيير amount إلى 50000.00 أو vendor_id إلى معرف خاص به.
كانت API معالج الدفعات تتطلب توقيع كل طلب باستخدام HMAC-SHA512. تقوم المنصة بتحويل حمولة JSON إلى سلسلة نصية قياسية (canonical)، وتحسب التوقيع باستخدام مفتاح API الخاص بها، وترسله في هيدر Authorization. تقوم خوادم معالج الدفعات بنفس الخطوات تمامًا. إذا تطابق توقيعها المحسوب مع التوقيع المرسل في الهيدر، فإنهم يعرفون شيئين على وجه اليقين: أن الطلب جاء من المنصة الشرعية (الموثوقية) وأن vendor_id و amount لم يتم تغييرهما أثناء النقل (السلامة).
الدرس المستفاد: بالنسبة للعمليات عالية المخاطر، يوفر HMAC طبقة أمان حيوية لضمان أن مرسل ومحتوى الرسالة هما ما تتوقعه تمامًا.
أخطاء وفخاخ شائعة
- تسريب المفتاح السري. المفتاح هو كل شيء. إذا كشفته في كود JavaScript من جانب العميل، أو قمت برفعه إلى مستودع Git عام، أو سجلته كنص عادي في ملفات السجل (logs)، فإن أمانك قد انتهى. عامله ككلمة مرور.
- استخدام مقارنة غير ثابتة الوقت. عندما تتحقق مما إذا كان التوقيع المقدم من المستخدم يطابق توقيعك المحسوب، فإن مقارنة السلاسل النصية القياسية مثل
if (a === b)يمكن أن تكون ثغرة أمنية. غالبًا ما تعيدfalseبمجرد العثور على أول حرف غير متطابق. هذا يخلق فرقًا زمنيًا ضئيلًا يمكن للمهاجمين قياسه لتخمين التوقيع حرفًا بحرف. هذا يسمى "هجوم التوقيت" (timing attack). استخدم دائمًا دالة مقارنة مخصصة "ثابتة الوقت" (constant-time) من مكتبة تشفير تستغرق نفس القدر من الوقت بغض النظر عن مكان حدوث عدم التطابق. - توقيع البيانات الخاطئة. يجب على المرسل والمستقبل حساب HMAC على نفس تسلسل البايتات بالضبط. من الأخطاء الشائعة أن يقوم أحد الطرفين بتوقيع كائن JSON منسق بشكل جميل بينما يوقع الطرف الآخر على النسخة المدمجة ذات السطر الواحد. أو أن يضيف أحد الطرفين سطرًا جديدًا في النهاية والآخر لا يفعل ذلك. يجب أن تتفقا على تنسيق رسالة قياسي (canonical) وتلتزما به.
- نسيان هجمات الإعادة (replay attacks). HMAC بحد ذاته لا يمنع المهاجم من التقاط رسالة صالحة وموقعة وإعادة إرسالها مرارًا وتكرارًا. إذا كانت تلك الرسالة هي "ادفع لبوب 10 دولارات"، فأنت لا تريد أن يتمكن المهاجم من تفعيل هذا الدفع 1000 مرة. لمنع ذلك، قم بتضمين قيمة تتغير مع كل طلب—مثل طابع زمني أو "nonce" (رقم يستخدم مرة واحدة)—داخل البيانات التي يتم توقيعها. يمكن للخادم بعد ذلك التحقق من الطابع الزمني لرفض الطلبات القديمة أو الاحتفاظ بقائمة من الـ nonces المستخدمة لرفض التكرارات.
لماذا يجب أن تكون على رادارك
يجب أن تفكر في HMAC كلما تعاملت مع اتصالات تحتاج إلى أن تكون موثوقة. الأمر لا يتعلق بالحفاظ على سرية البيانات (هذه وظيفة التشفير)، بل بضمان أن البيانات شرعية.
- هل تبني أو تستهلك APIs؟ خاصة مع الـ webhooks (من خدمات مثل Stripe، GitHub، Twilio)، يعد HMAC المعيار الصناعي للتحقق من أن الطلب أصلي.
- هل تعمل مع أنظمة المصادقة؟ العديد من الأنظمة القائمة على التوكن (token)، وأشهرها JWTs، تستخدم HMAC (مثل خوارزمية 'HS256') لتوقيع حمولة التوكن، مما يمنع المستخدمين من تعديل صلاحياتهم بأنفسهم.
- هل تحتاج إلى التحقق من سلامة البيانات؟ إذا كنت تمرر بيانات عبر بيئة غير موثوق بها (مثل متصفح المستخدم في كوكي) وتحتاج إلى التأكد من أنها تعود دون تعديل، فإن HMAC هو أداتك.
إنه أداة أساسية في صندوق أدوات الأمان لمطور الويب. فهم كيفية عمله سيجعلك مهندسًا أفضل وأكثر وعيًا بالأمان.
تعمق أكثر
- RFC 2104: HMAC: Keyed-Hashing for Message Authentication: المواصفات الفنية الأصلية. إنها كثيفة، لكنها المصدر النهائي للحقيقة.
- Wikipedia: HMAC: نظرة عامة ممتازة وعالية المستوى على التاريخ ومبادئ التصميم وتفاصيل التنفيذ.
- OWASP: Replay Attack: صفحة مشروع أمان تطبيقات الويب المفتوحة (OWASP) حول هجمات الإعادة، وهو مفهوم بالغ الأهمية يجب فهمه عند استخدام HMAC.
- Stripe Docs: Checking webhook signatures: دليل عملي من واقع الحياة من شركة تعتمد بشكل كبير على HMAC لتأمين معاملات بمليارات الدولارات.
- Crypto 101: HMAC: شرح مبسط إلى حد ما للمبادئ التشفيرية وراء تصميم HMAC.