طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 243 · أمن الويب
الدرس 22 · الوحدة 10: تقنيات التشفير

التجزئة وسلامة البيانات

كيف نكشف أن بايتًا واحدًا تغيّر، ونثبت أن الرسالة ممن نثق به؟ ⏱ نحو 45 دقيقة.

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

١التجزئة: بصمة أحادية الاتجاه

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

const express = require('express');const app = express();app.use(express.json());app.use(express.urlencoded({ extended: false })); const crypto = require('crypto'); app.get('/hash', (req, res) => {  const data = req.query.data || '';  res.json({    sha256: crypto.createHash('sha256').update(data).digest('hex'),  });}); app.get('/hmac', (req, res) => {  const key = 'shared-secret';  const msg = req.query.msg || '';  const tag = crypto.createHmac('sha256', key).update(msg).digest('hex');  res.json({ msg, tag });            // العلامة تثبت أن الرسالة من يملك المفتاح ولم تُعبث}); app.listen(3000);

حرف واحد اختلف (r كبيرة) فتغيّرت البصمة تمامًا. هذا «أثر الانهيار»: يجعل التجزئة مثاليّة للكشف عن أي تعديل.

التجزئة Hashالتشفير Encryption
الاتجاهأحادي (لا يُعكس)ثنائي (يُفكّ بالمفتاح)
المفتاحلا يلزم (أو مفتاح في HMAC)يلزم
الاستخدامبصمة، سلامة، كلمات المرورإخفاء المحتوى

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

٢HMAC: سلامة وأصل بمفتاح

التجزئة وحدها تكشف التعديل العشوائي، لكن مهاجمًا يغيّر الرسالة ويعيد حساب بصمتها. HMAC يحلّ هذا: تجزئة بمفتاح سرّي مشترك، فلا يحسب العلامة الصحيحة إلا من يملك المفتاح:

const express = require('express');const app = express();app.use(express.json());app.use(express.urlencoded({ extended: false })); const crypto = require('crypto'); app.get('/hash', (req, res) => {  const data = req.query.data || '';  res.json({    sha256: crypto.createHash('sha256').update(data).digest('hex'),  });}); app.get('/hmac', (req, res) => {  const key = 'shared-secret';  const msg = req.query.msg || '';  const tag = crypto.createHmac('sha256', key).update(msg).digest('hex');  res.json({ msg, tag });            // العلامة تثبت أن الرسالة من يملك المفتاح ولم تُعبث}); app.listen(3000);

لكل رسالة علامة HMAC. المهاجم الذي يبدّل 500 إلى 5000 لا يعرف المفتاح، فلا ينتج العلامة الصحيحة. المتلقّي يحسب العلامة ويقارن: إن اختلفت، رُفضت الرسالة.

يقدّم HMACالمعنى
السلامةلم تُعدَّل الرسالة
الأصلمن يملك المفتاح هو من أرسلها

يُستخدم HMAC في توقيع JWT (HS256 هو HMAC-SHA256)، وفي روابط موقّعة، وفي التحقق من الرسائل بين خدمتين تتشاركان مفتاحًا.

٣قارن بأمان

عند مقارنة علامة HMAC، لا تستخدم المقارنة العادية === على الأسرار الحسّاسة؛ زمنها يتسرّب معلومة. استخدم مقارنة ثابتة الزمن:

const a = Buffer.from(tag);const b = Buffer.from(expected);const ok = a.length === b.length &&  crypto.timingSafeEqual(a, b);   // لا يتوقّف عند أول اختلاف

توقّع وتحقّق

دالة التجزئة:

HMAC يقدّم:

لمقارنة علامة سرّية:

لكلمات المرور نستخدم:

الخلاصة

تحدٍّ برمجي: تحقّق من علامة HMAC

متقدم

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

المطلوب:
  • احسب HMAC-SHA256 للرسالة بالمفتاح shared-secret.
  • إن طابق tag المُرسَل: 200، وإلا 401.
👀 اعرض الاستجابات المطلوبة
✓ التحقق الآلي: يشغّل المختبر خادمك من جديد ويرسل إليه هذه الطلبات بالترتيب ثم يفحص الردود
  1. POST /verify {"msg": "hello", "tag": "dad60f181b1f0d72f63b2dff4e9aa1de277989cb1e08a8b0e64e43f774ac036e"}
  2. POST /verify {"msg": "hacked", "tag": "dad60f181b1f0d72f63b2dff4e9aa1de277989cb1e08a8b0e64e43f774ac036e"}
  • الخادم يعمل ويرد على كل الطلبات بلا أخطاء غير معالَجة
  • الرسالة بعلامتها الصحيحة تُقبل ← 200
  • رسالة معبوث بها بعلامة قديمة تُرفض ← 401
  • يحسب HMAC

💡 تلميح 1
احسب العلامة المتوقَّعة: crypto.createHmac('sha256', key).update(req.body.msg).digest('hex').
💡 تلميح 2
قارنها بـ req.body.tag.
💡 تلميح 3
إن اختلفت ردّ 401؛ المهاجم لا يملك المفتاح ليحسب علامة الرسالة المعدّلة.

حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.

const express = require('express');const app = express();app.use(express.json());app.use(express.urlencoded({ extended: false })); const crypto = require('crypto');app.post('/verify', (req, res) => {  const key = 'shared-secret';  const expected = crypto.createHmac('sha256', key).update(req.body.msg || '').digest('hex');  const ok = req.body.tag === expected;  res.status(ok ? 200 : 401).json({ accepted: ok });});app.listen(3000);
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

📝 معلم؟ أرسل ملاحظة للمؤلف عن هذا الدرس