Files
Siro/auth_flow_admin_staff.md
2026-06-12 20:40:40 +03:00

41 KiB

سير عمل تسجيل الدخول في نظام Siro

توثيق شامل لتدفق المصادقة للمشرفين (Super Admin / Admin) وموظفي خدمة العملاء (Service Staff)


📋 فهرس المحتويات

  1. نظرة عامة على النظام
  2. المكونات الرئيسية
  3. سير عمل تسجيل حساب مشرف جديد
  4. سير عمل تسجيل الدخول (المشرف - siro_admin)
  5. سير عمل تسجيل الدخول (موظف الخدمة - siro_service)
  6. سير عمل إضافة الموظفين من قبل المشرف العام
  7. سير عمل تفعيل الحسابات المعلقة
  8. مقارنة بين تدفق siro_admin و siro_service
  9. الجوانب الأمنية (Security)
  10. قائمة الإصلاحات الأمنية المُنفَّذة
  11. الإجراءات الموصى بها للتحسين (Recommendations)

نظرة عامة على النظام

نظام Siro يستخدم بنية متعددة التطبيقات (Multi-App Architecture):

التطبيق الدور ملفات المصادقة
siro_admin المشرفون (Admin / Super Admin) siro_admin/lib/views/auth/login_page.dart, otp_helper.dart
siro_service موظفو خدمة العملاء siro_service/lib/controller/login_controller.dart
Backend (PHP) الخادم المركزي backend/Admin/auth/login.php, backend/serviceapp/login.php

المبدأ الأساسي: المصادقة متعددة العوامل (MFA) تعتمد على:

  1. بصمة الجهاز (Fingerprint) — يتم إنشاؤها في التطبيق وإرسالها مع كل طلب
  2. كلمة المرور — مشفرة باستخدام password_hash
  3. OTP عبر WhatsApp — للتحقق الإضافي (للمشرفين حصراً)
  4. JWT — التوكن النهائي للوصول

المكونات الرئيسية

تطبيق siro_admin (المشرفون)

الملف المسار الوظيفة
login_page.dart siro_admin/lib/views/auth/ واجهة تسجيل الدخول (كلمة مرور + هاتف اختياري)
register_page.dart siro_admin/lib/views/auth/ واجهة طلب حساب مشرف جديد
otp_helper.dart siro_admin/lib/controller/auth/ منطق OTP وجدولة إعادة الدخول (Auto Login)
register_controller.dart siro_admin/lib/controller/auth/ منطق التسجيل

تطبيق siro_service (خدمة العملاء)

الملف المسار الوظيفة
login_controller.dart siro_service/lib/controller/ منطق تسجيل الدخول مع OTP
(لا يوجد register) التسجيل يتم عبر backend/serviceapp/register.php أو عبر add.php

الباك إند (PHP)

الملف المسار الوظيفة
login.php backend/Admin/auth/ تسجيل دخول المشرف (الخطوة الأولى)
verify_login.php backend/Admin/auth/ التحقق من OTP للمشرف (الخطوة الثانية)
register.php backend/Admin/auth/ تسجيل مشرف جديد
login.php backend/serviceapp/ تسجيل دخول موظف الخدمة
register.php backend/serviceapp/ تسجيل موظف خدمة جديد
add.php backend/Admin/Staff/ إضافة موظف/مشرف من قبل المشرف العام
pending.php backend/Admin/Staff/ جلب الحسابات المعلقة
activate.php backend/Admin/Staff/ تفعيل الحسابات المعلقة
jwtService.php backend/Admin/jwtService.php إصدار JWT لخدمة العملاء (قديم)
JwtService.php backend/core/Auth/ خدمة JWT الأساسية مع Authentication

سير عمل تسجيل حساب مشرف جديد

📍 يبدأ من تطبيق siro_admin ← register_page.dart

[المستخدم]                    [التطبيق (Flutter)]                       [الباك إند (PHP)]                [قاعدة البيانات]
    |                              |                                          |                              |
    |── يدخل الاسم، الهاتف،        |                                          |                              |
    |   كلمة المرور                |                                          |                              |
    |─────────────────────────────>|                                          |                              |
    |                              |                                          |                              |
    |                              |── قراءة بصمة الجهاز (fingerprint)        |                              |
    |                              |   من `box.read('fingerprint')`           |                              |
    |                              |                                          |                              |
    |                              |── POST `/Admin/auth/register.php`        |                              |
    |                              |   مع: name, phone, password, fingerprint |                              |
    |                              |──────────────────────────────────────────>|                              |
    |                              |                                          |                              |
    |                              |                                          |── التحقق من القائمة البيضاء  |
    |                              |                                          |   `AUTHORIZED_ADMIN_PHONES`   |
    |                              |                                          |   في متغيرات البيئة (.env)    |
    |                              |                                          |                              |
    |                              |                                          |── التحقق من التكرار:         |
    |                              |                                          |   phone OR fingerprint_hash  |
    |                              |                                          |                              |
    |                              |                                          |── تشفير البيانات:            |
    |                              |                                          |   • name ← encryptData()     |
    |                              |                                          |   • phone ← encryptData()    |
    |                              |                                          |   • fingerprint ← encrypt()  |
    |                              |                                          |   • fingerprint_hash =       |
    |                              |                                          |     SHA-256(fingerprint)     |
    |                              |                                          |   • password ← password_hash |
    |                              |                                          |                              |
    |                              |                                          |── توليد UUID آمن:            |
    |                              |                                          |   bin2hex(random_bytes(16))  |
    |                              |                                          |                              |
    |                              |                                          |── INSERT INTO adminUser      |
    |                              |                                          |   status = 'pending'         |
    |                              |                                          |   role = 'admin'             |
    |                              |                                          |─────────────────────────────>|  (id, fingerprint, fingerprint_hash,
    |                              |                                          |                              |   name, phone, password, role,
    |                              |                                          |                              |   status='pending', created_at)
    |                              |                                          |                              |
    |                              |<── { status: "pending", message: "..." }  |                              |
    |                              |                                          |                              |
    |<── رسالة "تم تقديم الطلب"    |                                          |                              |

ملاحظات أمنية حول التسجيل:

  • القائمة البيضاء (AUTHORIZED_ADMIN_PHONES) تمنع أي شخص من التسجيل دون إذن مسبق
  • الاسم والهاتف والبصمة مشفرة في قاعدة البيانات
  • بصمة الجهاز محولة إلى SHA-256 Hash للبحث السريع
  • bin2hex(random_bytes(16)) لتوليد UUID آمن — تم إصلاحه من rand()
  • 🔴 الحساب ينشأ بحالة pending ولا يمكنه الدخول حتى يتم تفعيله يدوياً

سير عمل تسجيل الدخول (المشرف - siro_admin)

📍 يبدأ من login_page.dartOtpHelper.loginWithPassword()

الخطوة الأولى: إرسال طلب الدخول

[المستخدم]            [otp_helper.dart]                  [login.php (Backend)]             [Redis / DB]
    |                       |                                   |                              |
    |── يدخل كلمة المرور    |                                   |                              |
    |   (ورقم الهاتف لأول   |                                   |                              |
    |    مرة)               |                                   |                              |
    |──────────────────────>|                                   |                              |
    |                       |                                   |                              |
    |                       |── POST /Admin/auth/login.php      |                              |
    |                       |   payload: fingerprint,           |                              |
    |                       |   password, phone (اختياري),      |                              |
    |                       |   aud (اختياري), is_renewal       |                              |
    |                       |──────────────────────────────────>|                              |
    |                       |                                   |                              |
    |                       |                                   |── Rate Limiting:             |
    |                       |                                   |   5 محاولات/دقيقة لكل IP    |
    |                       |                                   |                              |
    |                       |                                   |── البحث بـ fingerprint_hash  |
    |                       |                                   |   (SHA-256 للبصمة)           |
    |                       |                                   |                              |
    |                       |      [إذا لم يوجد بالبصمة والهاتف موجود]                          |
    |                       |                                   |── البحث بالهاتف المشفر       |
    |                       |                                   |   (لأول مرة أو جهاز جديد)    |
    |                       |                                   |                              |
    |                       |                                   |── التحقق من status:          |
    |                       |                                   |   • pending → رفض مع رسالة   |
    |                       |                                   |   • suspended → رفض          |
    |                       |                                   |   • rejected → رفض           |
    |                       |                                   |   • active → متابعة          |
    |                       |                                   |                              |
    |                       |                                   |── التحقق من كلمة المرور:     |
    |                       |                                   |   password_verify(password)  |
    |                       |                                   |                              |

التفرع حسب is_renewal:

                                   |
                   ┌───────────────┴───────────────┐
                   |                                |
              is_renewal=1                    is_renewal=0 أو missing
           (إعادة دخول تلقائي)                 (تسجيل دخول يدوي)
                   |                                |
                   |                                ├── توليد OTP عشوائي
                   |                                |   (100000-999999) ← 6 أرقام
                   |                                |
                   |                                ├── فك تشفير رقم الهاتف
                   |                                |   ← إرسال OTP عبر WhatsApp
                   |                                |
                   |                                ├── حفظ OTP مشفراً:
                   |                                |   `token_verification_admin`
                   |                                |   (phone مشفر، token مشفر،
                   |                                |    expiration 10 دقائق)
                   |                                |
                   |                                └── رد: { status: "otp_required",
                   |                                        phone: masked }
                   |
                   ├── إلغاء التوكن القديم من Redis
                   |   (Token Revocation)
                   |
                   ├── توليد JWT جديد:
                   |   generateAccessToken(id, role, aud, fingerprint)
                   |
                   └── رد مباشر: { jwt, admin, expires_in }

الخطوة الثانية (عند طلب OTP): التحقق ← verify_login.php

[otp_helper.dart]              [verify_login.php]                    [DB / Redis]
       |                              |                                  |
       |── POST /Admin/auth/         |                                  |
       |   verify_login.php          |                                  |
       |   payload: otp,             |                                  |
       |   fingerprint, phone        |                                  |
       |────────────────────────────>|                                  |
       |                              |                                  |
       |                              |── Rate Limiting:                |
       |                              |   3 محاولات OTP/5 دقائق لكل IP |
       |                              |                                  |
       |                              |── البحث بالـ fingerprint_hash   |
       |                              |   من جدول adminUser             |
       |                              |                                  |
       |                              |── تشفير OTP المدخل              |
       |                              |   encryptData(otp)              |
       |                              |                                  |
       |                              |── البحث في token_verification:  |
       |                              |   phone_number = encryptedPhone |
       |                              |   AND token = encryptedOtp      |
       |                              |   AND expiration >= NOW()       |
       |                              |                                  |
       |     [غير صالح أو منتهي]      |                                  |
       |<── "رمز التحقق غير صالح"     |                                  |
       |                              |                                  |
       |     [صالح]                   |                                  |
       |                              |── حذف OTP (استخدام لمرة واحدة)  |
       |                              |   DELETE FROM token_verification |
       |                              |                                  |
       |                              |── إلغاء التوكن القديم من Redis   |
       |                              |                                  |
       |                              |── توليد JWT جديد:                |
       |                              |   generateAccessToken(id, role,  |
       |                              |   aud, fingerprint)             |
       |                              |                                  |
       |<── { jwt, admin, expires_in }|                                  |
       |                              |                                  |
       |── حفظ البيانات محلياً:       |                                  |
       |   • JWT ← box.write(jwt)    |                                  |
       |   • admin_id ← box.write    |                                  |
       |   • admin_role ← box.write  |                                  |
       |   • phoneVerified ← true    |                                  |
       |   • admin_password ← حفظ     |                                  |
       |                              |                                  |
       |── Get.offAll(AdminHomePage())|                                  |

مخطط الحالة الكامل للمشرف (State Machine):

                        ┌───────────┐
                        │  Pending  │  ← بعد التسجيل، بحاجة تفعيل
                        └─────┬─────┘
                              │ Activate.php (يتطلب JWT مع role=admin)
                              ▼
                     ┌─────────────────┐
                     │     Active      │  ← يمكنه تسجيل الدخول
                     └────────┬────────┘
                              │
                    ┌─────────┴──────────┐
                    │                    │
                    ▼                    ▼
             ┌───────────┐        ┌───────────┐
             │ Suspended │        │ Rejected  │
             └───────────┘        └───────────┘

سير عمل تسجيل الدخول (موظف الخدمة - siro_service)

📍 يبدأ من login_controller.dartlogin()

[LoginController]             [serviceapp/login.php]                [DB / Redis]
       |                              |                                  |
       |── قراءة البصمة من            |                                  |
       |   box.read(fingerprint)      |                                  |
       |                              |                                  |
       |── POST /serviceapp/login.php |                                  |
       |   payload: fingerprint,      |                                  |
       |   password, email,           |                                  |
       |   aud = "service"            |                                  |
       |────────────────────────────>|                                  |
       |                              |                                  |
       |                              |── Rate Limiting:                |
       |                              |   5 محاولات/دقيقة لكل IP       |
       |                              |                                  |
       |                              |── البحث بـ fingerprint_hash      |
       |                              |   في `users` WHERE user_type     |
       |                              |   = 'service'                    |
       |                              |                                  |
       |      [إذا لم يوجد, والإيميل موجود]                               |
       |                              |── البحث بالإيميل المشفر         |
       |                              |                                  |
       |                              |── التحقق من status:             |
       |                              |   • pending → "قيد المراجعة"    |
       |                              |   • suspended → "معلق"          |
       |                              |   • approved → متابعة           |
       |                              |                                  |
       |                              |── التحقق من كلمة المرور         |
       |                              |                                  |
       |                              |── فك تشفير البيانات للعرض:      |
       |                              |   first_name, last_name,        |
       |                              |   email, phone                  |
       |                              |                                  |
       |                              |── إدارة التوكنات:               |
       |                              |   1. البحث عن توكن موجود في     |
       |                              |      Redis (active_token)       |
       |                              |   2. إذا وجد وصالح ← استخدامه   |
       |                              |   3. إذا لم يوجد ← إلغاء القديم |
       |                              |      وتوليد جديد                |
       |                              |                                  |
       |                              |── توليد HMAC Key:               |
       |                              |   hmac = hash_hmac(sha256,      |
       |                              |   userId, SECRET_KEY_HMAC)      |
       |                              |                                  |
       |<── { message, data, jwt,     |                                  |
       |      hmac, expires_in }      |                                  |
       |                              |                                  |
       |── إرسال OTP:                 |                                  |
       |   POST /auth/otp/request.php |                                  |
       |   payload: receiver=phone,   |                                  |
       |   user_type='service'        |                                  |
       |                              |                                  |
       |── عرض Dialog OTP             |                                  |
       |                              |                                  |
       |── التحقق من OTP:             |                                  |
       |   POST /auth/otp/verify.php  |                                  |
       |   payload: phone_number,     |                                  |
       |   token_code, user_type      |                                  |
       |                              |                                  |
       |── حفظ JWT + HMAC محلياً      |                                  |
       |                              |                                  |
       |── Get.offAll(Main())         |                                  |

سير عمل إضافة الموظفين من قبل المشرف العام

📍 backend/Admin/Staff/add.php

يستخدم هذا الملف لإضافة موظفين جدد من قبل المشرف العام (Super Admin أو Admin فقط).

                    ┌─────────────────────────────────────┐
                    │  add.php                            │
                    │                                     │
                    │  ✅ JWT Authentication:              │
                    │     $jwtService = new JwtService     │
                    │     $auth = $jwtService->authenticate│
                    │     $authRole = $auth->role          │
                    │     if role ≠ super_admin || admin → │
                    │         رفض الطلب                    │
                    └──────────────────┬──────────────────┘
                                       │
                         ──────────────┼──────────────
                     role='admin'      │      role='service'
                                       │
             ┌─────────────────────────┴──────────────────────────┐
             ▼                                                     ▼
     ┌────────────────┐                               ┌──────────────────┐
     │  adminUser     │                               │     users        │
     │  table         │                               │  table           │
     ├────────────────┤                               ├──────────────────┤
     │ id (bin2hex)   │                               │ id (bin2hex)     │
     │ fingerprint    │                               │ fingerprint      │
     │ fingerprint_   │                               │ fingerprint_hash │
     │ hash           │                               │ phone (مشفر)      │
     │ name (مشفر)    │                               │ email (مشفر)      │
     │ phone (مشفر)   │                               │ gender           │
     │ password       │                               │ password         │
     │ role 'admin'   │                               │ birthdate        │
     │ created_at     │                               │ user_type        │
     └────────────────┘                               │ 'service'         │
                                                      │ first_name (مشفر) │
                                                      │ last_name         │
                                                      │ site              │
                                                      │ created_at        │
                                                      └──────────────────┘

🔐 آلية التحقق في add.php (بعد الإصلاح):

$jwtService = new JwtService($redis);
$auth = $jwtService->authenticate();    // يقرأ Bearer token من Authorization header
$authRole = $auth->role ?? '';          // يستخرج الدور من JWT payload
if ($authRole !== 'super_admin' && $authRole !== 'admin') {
    jsonError("غير مصرح لك. فقط المشرفون يمكنهم إضافة موظفين.");
    exit;
}

نقاط مهمة في add.php:

  • bin2hex(random_bytes(16)) لتوليد UUID آمن
  • البيانات الحساسة مشفرة قبل الحفظ
  • fingerprint اختياري — يمكن إضافة موظف بدون بصمة مسبقة
  • التحقق من JWT Authentication مطلوب قبل الإضافة

سير عمل تفعيل الحسابات المعلقة

📍 pending.phpactivate.php

استعراض الحسابات المعلقة:

[pending.php]                    [قاعدة البيانات]
      │                                │
      │── SELECT FROM adminUser        │
      │   WHERE status='pending'       │
      │                                │
      │── SELECT FROM users            │
      │   WHERE status='pending'       │
      │   AND user_type='service'      │
      │                                │
      │── فك تشفير الأسماء والأرقام    │
      │                                │
      │── دمج النتائج (array_merge)    │
      │                                │
      │<── { data: [all_pending] }     │

تفعيل حساب:

[activate.php]                   [قاعدة البيانات]
      │                                │
      │── التحقق من JWT:               │
      │   $jwtService = new JwtService  │
      │   $auth = $jwtService->authenticate│
      │   $authRole = $auth->role       │
      │   if ≠ super_admin && ≠ admin → │
      │       رفض                       │
      │                                │
      │── POST مع: user_id, type       │
      │                                │
      │   type='admin':                │
      │   UPDATE adminUser             │
      │   SET status='active'          │
      │   WHERE id=user_id             │
      │   AND status='pending'         │
      │                                │
      │   type='service':              │
      │   UPDATE users                 │
      │   SET status='approved'        │
      │   WHERE id=user_id             │
      │   AND status='pending'         │
      │   AND user_type='service'      │
      │                                │
      │<── "تم التفعيل بنجاح"          │

🔐 آلية التحقق في activate.php (بعد الإصلاح):

$jwtService = new JwtService($redis);
$auth = $jwtService->authenticate();
$authRole = $auth->role ?? '';
if ($authRole !== 'super_admin' && $authRole !== 'admin') {
    jsonError("غير مصرح لك. فقط المشرف العام يمكنه تفعيل الحسابات.");
    exit;
}

مقارنة بين تدفق siro_admin و siro_service

الخاصية siro_admin (المشرف) siro_service (خدمة العملاء)
جدول البيانات adminUser users
مفتاح البحث بصمة الجهاز ← الهاتف بصمة الجهاز ← الإيميل
آلية OTP OTP عبر WhatsApp كخطوة قبل JWT JWT يصدر أولاً ثم OTP كخطوة تأكيد
Auto Login Yes (is_renewal=1) + JWT غير منتهي Yes (كلمة مرور مخزنة)
إلغاء التوكن القديم قبل إصدار الجديد (Redis) قبل إصدار الجديد (Redis)
عدد أرقام OTP 6 أرقام (مُحسَّن) لا يوجد OTP مدمج بالـ login
HMAC Key غير مستخدم يستخدم للتوافق مع CRUD
نوع التسجيل تسجيل ذاتي + قائمة بيضاء إضافة يدوية أو تسجيل ذاتي
الحالة عند الإنشاء pending pending / active

الجوانب الأمنية (Security)

نقاط القوة

  1. تشفير البيانات الحساسة — جميع PII (الاسم، الهاتف، الإيميل، البصمة) مشفرة في قاعدة البيانات بواسطة encryptData()
  2. بصمة الجهاز (Fingerprint) — ربط الحساب بجهاز معين يمنع الوصول من أجهزة غير معروفة
  3. إلغاء التوكن (Token Revocation) — التوكن القديم يُلغى قبل إصدار الجديد عبر Redis
  4. OTP لمدة محدودة — صلاحية 10 دقائق فقط للرمز
  5. استخدام لمرة واحدة — OTP يُحذف بعد التحقق
  6. القائمة البيضاء — التسجيل الذاتي مقيد بأرقام مصرح بها من .env
  7. Hash للبصمةSHA-256 للبحث السريع دون تخزين البصمة كما هي
  8. قناع رقم الهاتف — في الاستجابة، يظهر فقط 07XX***XXX
  9. UUID آمنbin2hex(random_bytes(16)) بدلاً من rand() (تم الإصلاح)
  10. JWT Authentication — لملفي add.php و activate.php (تم الإصلاح)
  11. Rate Limiting — على login و OTP (تم الإضافة)

الإصلاحات الأمنية المُنفَّذة

# الثغرة الملف الحالة
1 rand() لتوليد ID (ضعيف) Admin/auth/register.php تم الاستبدال بـ bin2hex(random_bytes(16))
2 التحقق من الصلاحيات معلق (Commented Out) Admin/Staff/add.php تم التفعيل عبر JwtService::authenticate()
3 لا يوجد تحقق من صلاحية المشرف العام Admin/Staff/activate.php تم إضافة JWT + التحقق من role
4 لا يوجد Rate Limiting على login Admin/auth/login.php تم الإضافة (5 محاولات/دقيقة)
5 لا يوجد Rate Limiting على login serviceapp/login.php تم الإضافة (5 محاولات/دقيقة)
6 لا يوجد Rate Limiting على OTP Admin/auth/verify_login.php تم الإضافة (3 محاولات/5 دقائق)
7 OTP 5 أرقام (ضعيف) Admin/auth/login.php تم الرفع إلى 6 أرقام (100000-999999)

الثغرات المتبقية (لم تُعالَج بعد)

الثغرة الموقع التأثير
كلمات مرور plain text (قديمة) Admin/jwtService.php (السطر 49) دعم كلمات المرور غير المشفرة للتوافق القديم
JWT Service مكرر Admin/jwtService.php و core/Auth/JwtService.php يوجد ملفان باسم JwtService بوظائف مختلفة
Auto Login بدون OTP Admin/auth/login.php مع is_renewal=1 إذا سُرقت البصمة وكلمة المرور، يمكن الدخول بدون OTP
كشف البصمة في الأخطاء otp_helper.dart لا يوجد تدقيق كافٍ في التقاط الأخطاء

قائمة الإصلاحات الأمنية المُنفَّذة

1. Admin/auth/register.php — استبدال ID الضعيف

قبل الإصلاح:

$uniqueId = rand(100000000, 999999999); // غير آمن، قد يتكرر

بعد الإصلاح:

$uniqueId = bin2hex(random_bytes(16)); // UUID آمن (32 حرف hex عشوائي)

2. Admin/Staff/add.php — تفعيل التحقق من الصلاحيات

قبل الإصلاح:

// التحقق معلق (Commented Out)
// $auth = JwtService::authenticate($redis);
// if ($auth['role'] !== 'super_admin' && $auth['role'] !== 'admin') { ... }

بعد الإصلاح:

$jwtService = new JwtService($redis);
$auth = $jwtService->authenticate();
$authRole = $auth->role ?? '';
if ($authRole !== 'super_admin' && $authRole !== 'admin') {
    jsonError("غير مصرح لك. فقط المشرفون يمكنهم إضافة موظفين.");
    exit;
}

3. Admin/Staff/activate.php — إضافة JWT Authentication

قبل الإصلاح:

// يجب التأكد من صلاحيات الـ Super Admin هنا
// (عادةً يتم التحقق من التوكن أو الـ Session)

بعد الإصلاح:

$jwtService = new JwtService($redis);
$auth = $jwtService->authenticate();
$authRole = $auth->role ?? '';
if ($authRole !== 'super_admin' && $authRole !== 'admin') {
    jsonError("غير مصرح لك. فقط المشرف العام يمكنه تفعيل الحسابات.");
    exit;
}

4. Admin/auth/login.php — إضافة Rate Limiting + رفع OTP إلى 6 أرقام

// Rate Limiting قبل بدء المعالجة
$rateLimiter = new RateLimiter($redis);
$rateLimiter->enforce(RateLimiter::identifier(), 'login');

// ... لاحقاً عند توليد OTP ...
$otp = rand(100000, 999999); // 6 أرقام بدلاً من 5

5. serviceapp/login.php — إضافة Rate Limiting

$rateLimiter = new RateLimiter($redis);
$rateLimiter->enforce(RateLimiter::identifier(), 'login');

6. Admin/auth/verify_login.php — إضافة Rate Limiting لـ OTP

$rateLimiter = new RateLimiter($redis);
$rateLimiter->enforce(RateLimiter::identifier(), 'otp'); // 3 محاولات/5 دقائق

خلاصة

graph TD
    subgraph "siro_admin (تطبيق المشرف)"
        A[Login Page] --> B[OtpHelper.loginWithPassword]
        B --> C{is_renewal?}
        C -->|نعم| D[JWT مباشر]
        C -->|لا| E[طلب OTP - 6 أرقام]
        E --> F[Verify OTP - Rate Limited]
        F --> G[JWT نهائي]
    end

    subgraph "Backend PHP - مؤمَّن"
        H[Admin/auth/login.php] --> RL1[Rate Limiter ✅]
        RL1 --> I[بحث بالبصمة/هاتف]
        I --> J[فحص الحالة]
        J --> K[التحقق من كلمة المرور]
        K --> L[توليد OTP 6-digits ✅ / JWT]
        M[verify_login.php] --> RL2[Rate Limiter (OTP) ✅]
        RL2 --> N[التحقق من OTP]
        N --> O[إصدار JWT]
        P[add.php] --> AUTH1[JWT Authentication ✅]
        Q[activate.php] --> AUTH2[JWT Authentication ✅]
    end

    subgraph "siro_service (تطبيق الخدمة)"
        R[LoginController.login] --> S[serviceapp/login.php]
        S --> RL3[Rate Limiter ✅]
        RL3 --> T[JWT + HMAC]
        T --> U[OTP للتأكيد]
        U --> V[تسجيل دخول نهائي]
    end

    B --> H
    F --> M
    R --> S

النظام مبني على أساس أمني قوي مع تشفير متعدد الطبقات. تم إصلاح 7 ثغرات أمنية:

  1. UUID آمن بدلاً من rand() في تسجيل المشرفين
  2. JWT Authentication في add.php — يمنع أي شخص غير مصرح له من إضافة موظفين
  3. JWT Authentication في activate.php — يضمن أن المشرف العام فقط هو من يمكنه التفعيل
  4. Rate Limiting (5 محاولات/دقيقة) على login.php للمشرفين
  5. Rate Limiting (5 محاولات/دقيقة) على login.php لخدمة العملاء
  6. Rate Limiting (3 محاولات/5 دقائق) على verify_login.php (OTP)
  7. رفع OTP إلى 6 أرقام لزيادة صعوبة التخمين

تاريخ التوثيق: 12 يونيو 2026
آخر تحديث: 12 يونيو 2026 (تم تنفيذ الإصلاحات)
الإصدار: 2.0
المراجعة القادمة:
المعنيون: فريق التطوير — Siro