طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 241 · تحليل وتصميم برمجيات الويب
الدرس 19 · الوحدة 9: تقييم وتحسين تصميم صلاحيات المستخدمين

مراجعة تصميم الصلاحيات: الثغرات الشائعة

كيف تقيّم تصميم صلاحيات قائم، وتكتشف ثغراته في التصميم والكود، وتكتب تقرير مراجعة؟ ⏱ نحو 45 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تتعرف على ثغرات تصميم الصلاحيات: التراكم، والانفجار، والدور الخارق، وغياب فصل المهام، والحسابات اليتيمة.
  • تتعرف على ثغرات التنفيذ: الثقة بالعميل، والإسناد الجماعي، والمسار المنسي.
  • تراجع نظامًا بقائمة تدقيق، وتكتب تقريرًا مرتبًا حسب الخطورة.
  • تصلح ثغرات حقيقية في خادم.

١لماذا نراجع تصميم الصلاحيات؟

التصميم الأول نادرًا ما يكون كاملًا، والأنظمة تتغير: أدوار تُضاف، وموظفون ينتقلون، ومسارات تُبرمج على عجل. من الخطة: «تقييم تصميم الصلاحيات الحالي وتحليله لضمان التناسب والتعامل مع التهديدات». المراجعة تفحص التصميم (المصفوفة والأدوار) والتنفيذ (الكود) معًا.

٢ثغرات في التصميم

الثغرةكيف تظهرالمعالجة
تراكم الصلاحياتموظفة انتقلت من التسجيل إلى المحاسبة وبقيت لها صلاحيات الدورينمراجعة دورية للإسناد، وسحب الدور القديم عند النقل
انفجار الأدوارأربعون دورًا لخمسين موظفًا، لا أحد يعرف الفرق بينهادمج الأدوار المتشابهة، والاستثناءات بشروط لا بأدوار
دور خارقدور «مدير» يملك كل شيء ويستخدمه كثيرون للسهولةتقسيمه، وحصره بأشخاص قليلين، وتسجيل كل استخدام
غياب فصل المهامالشخص نفسه يطلب الاسترداد ويعتمدهصلاحيتان منفصلتان لدورين مختلفين
حسابات يتيمةحسابات موظفين غادروا ما زالت فعالةإجراء إغلاق الحساب مع إنهاء العلاقة

٣ثغرات في التنفيذ

الثغرةمثالالصواب
الثقة بما يرسله العميلالدور في ترويسة أو حقل يرسله المتصفح: x-role: managerالدور من قاعدة البيانات عبر الرمز فقط
الإسناد الجماعيObject.assign(user, req.body) فيغيّر المستخدم دوره بنفسهتحديث الحقول المسموحة بالاسم
مسار منسيمسار الحذف بلا authorize لأنه أُضيف أخيرًاالمنع افتراضيًّا، واختبار كل مسار
الحماية في الواجهة فقطالزر مخفي والمسار مفتوحالتحقق في الخادم دائمًا
رسائل خطأ كاشفة«كلمة المرور خاطئة لهذا البريد» تكشف أن البريد مسجلرسالة عامة: «بيانات الدخول غير صحيحة»

٤قائمة تدقيق وتقرير مراجعة

المراجعة المنهجية تعتمد قائمة ثابتة تمر عليها في كل مرة، ثم تقرير يرتب ما وجدته حسب خطورته:

☑️ قائمة تدقيق الصلاحيات
  1. هل لكل مسار في الخادم قاعدة صلاحية صريحة؟ (قارن قائمة المسارات بالمصفوفة)
  2. هل تأتي الهوية والدور من الرمز وحده؟
  3. هل تُفحص الملكية في كل مسار فيه معرّف /:id؟
  4. هل تُحدَّث الحقول المسموحة فقط؟
  5. هل يملك كل دور الحد الأدنى؟ وهل يوجد دور خارق؟
  6. هل العمليات الحساسة مقسمة بين دورين؟
  7. هل تُسجَّل العمليات الحساسة؟ ومتى رُوجع الإسناد آخر مرة؟
📋 تقرير مراجعة: نظام «دورات» · نوفمبر 2026
#الملاحظةالخطورةالتوصية
1الدور يُقرأ من ترويسة x-roleحرجةإزالة الترويسة وقراءة الدور من المستخدم
2PATCH /api/me يقبل أي حقلحرجةتحديث الاسم فقط
3DELETE /api/courses/:id بلا تفويضعاليةإضافة course:delete للمدير فقط
4ثلاثة موظفين بدور مديرمتوسطةحصره في أ. فهد ونائبه

توقّع وتحقّق

موظفة انتقلت إلى المحاسبة وبقيت لها صلاحيات التسجيل. هذه:

الخادم يقرأ الدور من ترويسة يرسلها المتصفح. الخطورة:

أفضل رسالة عند فشل تسجيل الدخول:

لا تكشف وجود البريد.

كيف تتأكد أنه لا يوجد مسار منسي؟

الخلاصة

تحدٍّ برمجي: أصلح ما وجدته المراجعة

متقدم

هذا هو الخادم الذي كُتب عنه تقرير المراجعة في الدرس. أصلح الملاحظات الثلاث الأولى.

المطلوب:
  • الدور يأتي من بيانات المستخدم فقط؛ لا أثر لترويسة x-role.
  • PATCH /api/me يحدّث الاسم فقط، ولا يغيّر الدور أبدًا.
  • حذف الدورة يتطلب course:delete (للمدير فقط).
👀 اعرض الاستجابات المطلوبة
✓ التحقق الآلي: يشغّل المختبر خادمك من جديد ويرسل إليه هذه الطلبات بالترتيب ثم يفحص الردود
  1. GET /api/reports/finance 🔑
  2. PATCH /api/me {"name": "سارة أحمد", "role": "manager"} 🔑
  3. GET /api/reports/finance 🔑
  4. DELETE /api/courses/1 🔑
  5. DELETE /api/courses/1 🔑
  • الخادم يعمل ويرد على كل الطلبات بلا أخطاء غير معالَجة
  • الترويسة x-role لا تمنح سارة التقرير ← 403
  • الاسم تغيّر والدور بقي trainee
  • بعد محاولة الترقية ما زالت سارة ممنوعة ← 403
  • سارة لا تحذف دورة ← 403
  • أ. فهد يحذف ← deleted
  • لا قراءة لترويسة x-role
  • لا Object.assign مع req.body

💡 تلميح 1
في authenticate أعد السطر الأصلي: req.user = user;
💡 تلميح 2
في /api/me: if (req.body.name) u.name = req.body.name;
💡 تلميح 3
أضف authorize('course:delete') إلى مسار الحذف؛ والصلاحية موجودة للمدير في ROLE_PERMS.

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

const express = require('express');const app = express();app.use(express.json()); // من مصفوفة الصلاحيات (الدرس 16) إلى الكود مباشرةconst ROLE_PERMS = {  trainee:   ['course:read', 'enrollment:create', 'enrollment:read:own'],  trainer:   ['course:read', 'attendance:record'],  registrar: ['course:read', 'enrollment:read', 'enrollment:approve'],  manager:   ['course:read', 'course:delete', 'enrollment:read', 'report:finance', 'user:manage'],};const users = [  { id: 1, name: 'سارة', role: 'trainee' },  { id: 2, name: 'نورة', role: 'registrar' },  { id: 3, name: 'أ. فهد', role: 'manager' },  { id: 4, name: 'خالد', role: 'trainer' },];// رموز دخول مبسطة للتجربة؛ في النظام الحقيقي JWT كما في مقرر أطر الويبconst tokens = { 'tok-sara': 1, 'tok-noura': 2, 'tok-fahad': 3, 'tok-khaled': 4 }; // 1) المصادقة: من أنت؟function authenticate(req, res, next) {  const t = (req.headers.authorization || '').replace('Bearer ', '');  const user = users.find(u => u.id === tokens[t]);  if (!user) return res.status(401).json({ error: 'سجّل الدخول أولًا' });  req.user = user;  next();} // 2) التفويض: هل يحق لك؟const can = (user, perm) => (ROLE_PERMS[user.role] || []).includes(perm);function authorize(perm) {  return (req, res, next) =>    can(req.user, perm) ? next() : res.status(403).json({ error: 'لا تملك الصلاحية ' + perm });} const courses = [{ id: 1, title: 'تصميم الويب' }, { id: 2, title: 'قواعد البيانات' }]; app.get('/api/reports/finance', authenticate, authorize('report:finance'), (req, res) => {  res.json({ revenue: 12500 });}); app.patch('/api/me', authenticate, (req, res) => {  const u = users.find(x => x.id === req.user.id);  if (req.body.name) u.name = req.body.name;      // الحقول المسموحة فقط  res.json(u);}); app.delete('/api/courses/:id', authenticate, authorize('course:delete'), (req, res) => {  const i = courses.findIndex(c => c.id === Number(req.params.id));  if (i < 0) return res.status(404).json({ error: 'غير موجودة' });  courses.splice(i, 1);  res.json({ deleted: true });}); app.listen(3000);
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

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