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

إدارة الجلسات الآمنة

كيف يتذكّرك الخادم بأمان بعد الدخول، ولماذا نجدّد المعرّف كل مرة؟ ⏱ نحو 45 دقيقة.

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

١الجلسة: ذاكرة ما بعد الدخول

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

const express = require('express');const cookieParser = require('cookie-parser');const crypto = require('crypto');const app = express();app.use(cookieParser()); app.post('/login', (req, res) => {  const sid = crypto.randomBytes(16).toString('hex');   // معرّف عشوائي غير متوقَّع  res.cookie('sid', sid, {    httpOnly: true,     // لا يصله جافاسكربت (ضد XSS)    secure: true,       // عبر HTTPS فقط    sameSite: 'strict', // ضد CSRF    maxAge: 3600000,    // ينتهي بعد ساعة  });  res.json({ ok: true });}); app.listen(3000);

لاحظ ترويسة Set-Cookie: المعرّف عشوائي، والكوكي يحمل HttpOnly و Secure و SameSite=Strict و انتهاء. هذه الخصائص الأربع تحصّن الجلسة ضد XSS و CSRF والتنصّت.

الخاصيةتحمي من
httpOnlyقراءة الكوكي بجافاسكربت (XSS)
secureإرساله عبر HTTP غير مشفّر
sameSiteإرساله مع طلبات المواقع الأخرى (CSRF)
maxAge / انتهاءبقاء الجلسة حيّة إلى الأبد

٢تثبيت الجلسة: جدّد المعرّف عند الدخول

هجوم تثبيت الجلسة: يزرع المهاجم معرّف جلسة يعرفه في متصفح الضحية قبل الدخول، فإن لم يجدّد الخادم المعرّف عند تسجيل الدخول، صار المعرّف المعروف للمهاجم مصادَقًا باسم الضحية:

✗ بلا تجديد

✓ بتجديد

المهاجم يثبّت sid معروفًا في متصفح الضحية

الضحية تسجّل الدخول

المعرّف نفسه صار مصادَقًا — المهاجم يملكه

معرّف جديد — المثبَّت بلا قيمة

الدفاع: ولّد معرّفًا جديدًا عند كل تسجيل دخول.

القاعدة: لا تقبل معرّف جلسة من العميل، وولّد معرّفًا جديدًا عند كل رفع صلاحية (الدخول). وعند الخروج، أبطِل الجلسة في الخادم لا في المتصفح فقط.

الحدثالفعل الآمن
تسجيل الدخولمعرّف جديد عشوائي (ضد التثبيت)
الخروجحذف الجلسة من الخادم (إبطال فعلي)
الخمولانتهاء بعد مدة عدم نشاط
تغيير كلمة المرورإبطال كل الجلسات الأخرى

٣الرموز أم الجلسات؟

بديل جلسات الكوكي هو رموز JWT بلا حالة (الدرس 18). لكلٍّ موضعه:

جلسات الخادم (كوكي)رموز JWT بلا حالة
الإبطالفوري (احذفها من الخادم)صعب قبل انتهائها
الحالةيحفظها الخادمبلا حالة (قابل للتوسّع)
CSRFيحتاج SameSiteمحصّن إن كان في ترويسة Authorization

للتطبيقات التي تحتاج إبطالًا فوريًّا، جلسات الخادم أبسط. وللأنظمة الموزّعة الكبيرة، تُفضَّل الرموز مع قِصر عمرها وآلية تجديد. وأيًّا اخترت، طبّق خصائص الكوكي الآمنة والتجديد عند الدخول.

توقّع وتحقّق

معرّف الجلسة يجب أن يكون:

تجديد المعرّف عند الدخول يمنع:

خاصية الكوكي ضد قراءته بجافاسكربت:

الخروج الآمن:

الخلاصة

تحدٍّ برمجي: جلسة آمنة

متقدم

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

المطلوب:
  • ولّد sid عشوائيًّا في الخادم، ولا تأخذه من الطلب.
  • ضع الكوكي بـ httpOnly و secure و sameSite: 'strict'.
👀 اعرض الاستجابات المطلوبة
✓ التحقق الآلي: يشغّل المختبر خادمك من جديد ويرسل إليه هذه الطلبات بالترتيب ثم يفحص الردود
  1. POST /login {"sid": "attacker-fixed"}
  • الخادم يعمل ويرد على كل الطلبات بلا أخطاء غير معالَجة
  • الدخول يعمل ← 200
  • يضبط كوكي الجلسة
  • يولّد المعرّف عشوائيًّا
  • لا يأخذ المعرّف من جسم الطلب
  • كوكي httpOnly و sameSite

💡 تلميح 1
المعرّف: const sid = crypto.randomBytes(16).toString('hex');.
💡 تلميح 2
لا تستخدم req.body.sid إطلاقًا.
💡 تلميح 3
الكوكي: res.cookie('sid', sid, { httpOnly:true, secure:true, sameSite:'strict' }).

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

const express = require('express');const app = express();app.use(express.json());app.use(express.urlencoded({ extended: false })); const crypto = require('crypto');const sessions = {};app.post('/login', (req, res) => {  const sid = crypto.randomBytes(16).toString('hex');   // معرّف جديد من الخادم  sessions[sid] = { user: 'sara' };  res.cookie('sid', sid, { httpOnly: true, secure: true, sameSite: 'strict', maxAge: 3600000 });  res.json({ ok: true });});app.listen(3000);
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

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