إدارة الجلسات الآمنة
كيف يتذكّرك الخادم بأمان بعد الدخول، ولماذا نجدّد المعرّف كل مرة؟ ⏱ نحو 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 / انتهاء | بقاء الجلسة حيّة إلى الأبد |
٢تثبيت الجلسة: جدّد المعرّف عند الدخول
هجوم تثبيت الجلسة: يزرع المهاجم معرّف جلسة يعرفه في متصفح الضحية قبل الدخول، فإن لم يجدّد الخادم المعرّف عند تسجيل الدخول، صار المعرّف المعروف للمهاجم مصادَقًا باسم الضحية:
القاعدة: لا تقبل معرّف جلسة من العميل، وولّد معرّفًا جديدًا عند كل رفع صلاحية (الدخول). وعند الخروج، أبطِل الجلسة في الخادم لا في المتصفح فقط.
| الحدث | الفعل الآمن |
|---|---|
| تسجيل الدخول | معرّف جديد عشوائي (ضد التثبيت) |
| الخروج | حذف الجلسة من الخادم (إبطال فعلي) |
| الخمول | انتهاء بعد مدة عدم نشاط |
| تغيير كلمة المرور | إبطال كل الجلسات الأخرى |
٣الرموز أم الجلسات؟
بديل جلسات الكوكي هو رموز JWT بلا حالة (الدرس 18). لكلٍّ موضعه:
| جلسات الخادم (كوكي) | رموز JWT بلا حالة | |
|---|---|---|
| الإبطال | فوري (احذفها من الخادم) | صعب قبل انتهائها |
| الحالة | يحفظها الخادم | بلا حالة (قابل للتوسّع) |
| CSRF | يحتاج SameSite | محصّن إن كان في ترويسة Authorization |
للتطبيقات التي تحتاج إبطالًا فوريًّا، جلسات الخادم أبسط. وللأنظمة الموزّعة الكبيرة، تُفضَّل الرموز مع قِصر عمرها وآلية تجديد. وأيًّا اخترت، طبّق خصائص الكوكي الآمنة والتجديد عند الدخول.
توقّع وتحقّق
معرّف الجلسة يجب أن يكون:
تجديد المعرّف عند الدخول يمنع:
خاصية الكوكي ضد قراءته بجافاسكربت:
الخروج الآمن:
الخلاصة
- معرّف الجلسة عشوائي غير متوقَّع يُحفظ في كوكي محصّن.
- خصائص الكوكي:
httpOnlyوsecureوsameSiteو انتهاء. - جدّد المعرّف عند الدخول ضد تثبيت الجلسة، ولا تقبله من العميل.
- الخروج يبطل الجلسة في الخادم؛ وجلسات الخادم أسهل إبطالًا من
JWT.
تحدٍّ برمجي: جلسة آمنة
متقدممسار الدخول يقبل معرّف جلسة من العميل (تثبيت) ويضع كوكيًّا بلا حماية. أصلحه: معرّف جديد من الخادم، وكوكي بخصائص آمنة.
- ولّد
sidعشوائيًّا في الخادم، ولا تأخذه من الطلب. - ضع الكوكي بـ
httpOnlyوsecureوsameSite: 'strict'.
👀 اعرض الاستجابات المطلوبة
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);