676 lines
41 KiB
Markdown
676 lines
41 KiB
Markdown
<div dir="rtl" lang="ar">
|
|
|
|
# سير عمل تسجيل الدخول في نظام Siro
|
|
|
|
## توثيق شامل لتدفق المصادقة للمشرفين (Super Admin / Admin) وموظفي خدمة العملاء (Service Staff)
|
|
|
|
---
|
|
|
|
## 📋 فهرس المحتويات
|
|
|
|
1. [نظرة عامة على النظام](#نظرة-عامة-على-النظام)
|
|
2. [المكونات الرئيسية](#المكونات-الرئيسية)
|
|
3. [سير عمل تسجيل حساب مشرف جديد](#سير-عمل-تسجيل-حساب-مشرف-جديد)
|
|
4. [سير عمل تسجيل الدخول (المشرف - siro_admin)](#سير-عمل-تسجيل-الدخول-المشرف---siro_admin)
|
|
5. [سير عمل تسجيل الدخول (موظف الخدمة - siro_service)](#سير-عمل-تسجيل-الدخول-موظف-الخدمة---siro_service)
|
|
6. [سير عمل إضافة الموظفين من قبل المشرف العام](#سير-عمل-إضافة-الموظفين-من-قبل-المشرف-العام)
|
|
7. [سير عمل تفعيل الحسابات المعلقة](#سير-عمل-تفعيل-الحسابات-المعلقة)
|
|
8. [مقارنة بين تدفق siro_admin و siro_service](#مقارنة-بين-تدفق-siro_admin-و-siro_service)
|
|
9. [الجوانب الأمنية (Security)](#الجوانب-الأمنية-security)
|
|
10. [قائمة الإصلاحات الأمنية المُنفَّذة](#قائمة-الإصلاحات-الأمنية-المنفذة)
|
|
11. [الإجراءات الموصى بها للتحسين (Recommendations)](#الإجراءات-الموصى-بها-للتحسين-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.dart` ← `OtpHelper.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.dart` ← `login()`
|
|
|
|
```
|
|
[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 (بعد الإصلاح):
|
|
|
|
```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.php` → `activate.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 (بعد الإصلاح):
|
|
|
|
```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 الضعيف
|
|
|
|
**قبل الإصلاح:**
|
|
```php
|
|
$uniqueId = rand(100000000, 999999999); // غير آمن، قد يتكرر
|
|
```
|
|
|
|
**بعد الإصلاح:**
|
|
```php
|
|
$uniqueId = bin2hex(random_bytes(16)); // UUID آمن (32 حرف hex عشوائي)
|
|
```
|
|
|
|
### 2. ✅ `Admin/Staff/add.php` — تفعيل التحقق من الصلاحيات
|
|
|
|
**قبل الإصلاح:**
|
|
```php
|
|
// التحقق معلق (Commented Out)
|
|
// $auth = JwtService::authenticate($redis);
|
|
// if ($auth['role'] !== 'super_admin' && $auth['role'] !== 'admin') { ... }
|
|
```
|
|
|
|
**بعد الإصلاح:**
|
|
```php
|
|
$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
|
|
|
|
**قبل الإصلاح:**
|
|
```php
|
|
// يجب التأكد من صلاحيات الـ Super Admin هنا
|
|
// (عادةً يتم التحقق من التوكن أو الـ Session)
|
|
```
|
|
|
|
**بعد الإصلاح:**
|
|
```php
|
|
$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 أرقام
|
|
|
|
```php
|
|
// Rate Limiting قبل بدء المعالجة
|
|
$rateLimiter = new RateLimiter($redis);
|
|
$rateLimiter->enforce(RateLimiter::identifier(), 'login');
|
|
|
|
// ... لاحقاً عند توليد OTP ...
|
|
$otp = rand(100000, 999999); // 6 أرقام بدلاً من 5
|
|
```
|
|
|
|
### 5. ✅ `serviceapp/login.php` — إضافة Rate Limiting
|
|
|
|
```php
|
|
$rateLimiter = new RateLimiter($redis);
|
|
$rateLimiter->enforce(RateLimiter::identifier(), 'login');
|
|
```
|
|
|
|
### 6. ✅ `Admin/auth/verify_login.php` — إضافة Rate Limiting لـ OTP
|
|
|
|
```php
|
|
$rateLimiter = new RateLimiter($redis);
|
|
$rateLimiter->enforce(RateLimiter::identifier(), 'otp'); // 3 محاولات/5 دقائق
|
|
```
|
|
|
|
---
|
|
|
|
## خلاصة
|
|
|
|
```mermaid
|
|
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
|
|
|
|
</div> |