# تحليل تدفق تسجيل ودخول السائق (Siro Driver) ## تصفُّل كامل من Flutter إلى PHP Backend --- ## 1. الملفات المشاركة ### تطبيق Flutter — siro_driver | الملف | الدور | |-------|--------| | `controller/auth/captin/login_captin_controller.dart` | كل منطق تسجيل الدخول (Google, Credentials, JWT, OTP) | | `controller/auth/captin/register_captin_controller.dart` | كل منطق التسجيل (OTP, Verify, إرسال البيانات) | | `controller/auth/captin/opt_token_controller.dart` | OTP Token | | `controller/functions/crud.dart` | HTTP client مع NetGuard + JWT refresh | | `controller/functions/encrypt_decrypt.dart` | تشفير/فك تشفير محلي | | `controller/functions/device_info.dart` | Fingerprint | ### Backend PHP | الملف | الرابط المستخدم من التطبيق | |-------|---------------------------| | `auth/captin/loginFromGoogle.php` | `loginFromGoogleCaptin` | | `auth/captin/loginUsingCredentialsWithoutGoogle.php` | `loginUsingCredentialsWithoutGoogle` | | `loginFirstTimeDriver.php` | `loginFirstTimeDriver` | | `loginJwtDriver.php` | `loginJwtDriver` | | `auth/captin/login.php` | تسجيل دخول عادي (email + phone + password) | | `auth/Tester/` | `getTesterApp`, `updateTesterApp` | | `auth/otp/` | OTP APIs | --- ## 2. التدفق الكامل — تسجيل حساب جديد (Sign Up) ### 🧩 مسار السجلات: من البداية حتى النهاية ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ التطبيق (Flutter) │ الباك إند (PHP) │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ 1. onInit() │ │ ├── getJWT(): │ │ │ POST /loginFirstTimeDriver.php │ │ │ payload: { id: AK.newId, password: AK.passnpassenger, │ │ │ aud: "${AK.allowed}$dev", fingerPrint } │ │ │ ← يحصل على registration JWT (صلاحية 450 ثانية) │ │ │ └── يخزّن JWT في box + secure storage │ │ │ │ │ 2. تسجيل الدخول عبر Google (loginWithGoogleCredential) │ │ ├── GET /auth/captin/loginFromGoogle.php?driverID=X │ │ │ └── هذا الملف يستخدم connect.php (قديم) ← لا JWT check │ │ │ ← إذا found → يقرأ data من الـ DB │ │ │ ← يخزّن بيانات كاملة (firstName, lastName, phone, gender, │ │ │ carYear, model, bankCode, ...) في GetStorage │ │ │ ← يفحص driver token (getDriverToken) ← يقارن مع المخزّن │ │ │ ← إذا تغيّر fingerprint → يحوّل لصفحة OTP │ │ │ ← ينتقل لـ HomeCaptain │ │ └── إذا not found → isPhoneVerified() → RegistrationView │ │ │ │ 3. التسجيل (sendOtpMessage) │ │ ├── POST /auth/otp/sendVerifyOtpMessage │ │ │ payload: { phone_number, driverId, email } │ │ ├── POST /auth/otp/verifyOtpDriver (للسائق) │ │ │ payload: { phone_number, token_code } │ │ │ ← يخزّن phoneDriver, phoneVerified = 1 │ │ │ ← ينتقل لـ RegistrationView │ │ │ │ │ 4. الرفع للمستندات والصور (CarLicense, ID Card, إلخ) │ │ ├── POST /addLicense (رخصة القيادة) │ │ ├── POST /addRegisrationCar (تسجيل السيارة) │ │ └── CRUD.post /signUpCaptin (إنشاء الحساب النهائي) │ │ payload: { first_name, last_name, email, phone, password, │ │ gender, site, birthdate } │ │ ← يخزّن driverID, dob, sex, phone │ │ ← يرسل sendVerifyEmail (OTP للبريد) │ │ ← ينتقل لـ VerifyEmailCaptainPage │ │ │ │ 5. بعد التحقق من الإيميل → LoginCaptin (يعيد تسجيل الدخول) │ │ │ └─────────────────────────────────────────────────────────────────────────┘ ``` --- ## 3. التدفق الكامل — تسجيل الدخول (Login) ### مسار تسجيل الدخول بالإيميل وكلمة المرور ``` ┌────────────────────────────────────────────────────────────────────┐ │ Flutter │ PHP Backend │ ├────────────────────────────────────────────────────────────────────┤ │ │ │ loginUsingCredentialsWithoutGoogle(email, password): │ │ │ │ GET /auth/captin/loginUsingCredentialsWithoutGoogle.php │ │ ?email=...&password=... │ │ │ │ ← هذا الملف يستخدم connect.php (قديم، لا JWT check) │ │ │ │ خطوات الباك إند: │ │ 1. filterRequest('email') │ │ 2. تشفير الإيميل: encryptData($email) │ │ 3. SELECT من driver LEFT JOIN phone_verification │ │ WHERE email = :encryptedEmail │ │ AND phone_verification.is_verified = '1' │ │ 4. password_verify($password, $data['password']) │ │ 5. فك تشفير جميع الحقول (phone, email, gender, birthdate, ...) │ │ 6. إرجاع JSON { status: "success", data: {...} } │ │ │ │ في التطبيق: │ │ 1. يخزّن كل البيانات في box (driverID, email, phone, ...) │ │ 2. يخزّن fingerprint في secure storage │ │ 3. يحصل على driver token (getDriverToken) │ │ 4. يقارن الـ token مع المخزّن ← إذا تغير → Dialog "new device" │ │ 5. إذا matched → Get.off(HomeCaptain) │ │ │ └────────────────────────────────────────────────────────────────────┘ ``` ### مسار تسجيل الدخول عبر Google ``` ┌────────────────────────────────────────────────────────────────────┐ │ Flutter │ PHP Backend │ ├────────────────────────────────────────────────────────────────────┤ │ │ │ loginWithGoogleCredential(driverID, email): │ │ │ │ GET /auth/captin/loginFromGoogle.php?id=driverID │ │ │ │ ← هذا الملف يستخدم connect.php (قديم، لا JWT check) │ │ ← لا يستخدم email في البحث (معلق) — يبحث بـ id فقط │ │ │ │ خطوات الباك إند: │ │ 1. filterRequest('id') ← driverID │ │ 2. SELECT مع LEFT JOIN (phone_verification, CarRegistration, │ │ driver_gifts, invites) WHERE driver.id = :id │ │ 3. فك تشفير الحقول الحساسة │ │ 4. إرجاع { status, data: { phone, email, first_name, ... } } │ │ │ │ في التطبيق: │ │ 1. يخزّن بيانات كاملة في box │ │ 2. يحصل على JWT عبر getJWT() │ │ 3. يتحقق من driver token ← إذا تغير fingerprint → OTP Dialog │ │ 4. يدخل إلى HomeCaptain │ │ │ └────────────────────────────────────────────────────────────────────┘ ``` ### مسار الـ JWT (Two-Phase) ``` ┌────────────────────────────────────────────────────────────────────┐ │ Phase 1: Registration Token │ │ loginFirstTimeDriver.php │ │ ← Rate Limiter (5/دقيقة) ✅ │ │ ← Audience check (allowedDriver1/2) ✅ │ │ ← password_verify(password, passwordnewpassenger) ✅ │ │ ← Fingerprint + Pepper → SHA-256 ✅ │ │ ← JWT: token_type='registration', exp=450s (7.5 min) │ │ ← Secret Key من ملف خارجي (.secret_key) ✅ │ │ │ │ Phase 2: Access Token │ │ loginJwtDriver.php │ │ ← Rate Limiter (5/دقيقة) ✅ │ │ ← Audience check ✅ │ │ ← قراءة driver من DB (SELECT id, phone, national_number, password)│ │ ← فك تشفير phone و national_number │ │ ← بناء HMAC: sha256(id|phone|national_number, SECRET_KEY_HMAC) │ │ ← password_verify(hmacHex, driver.password) ✅ │ │ ← توليد Access Token (JwtService.generateAccessToken) │ │ ← إلغاء التوكن القديم عبر Redis (Token Revocation) ✅ │ │ ← إرجاع { jwt, expires_in: 14400 (4h) } │ │ │ │ في التطبيق (getJWT): │ │ إذا firstTimeLoadKey != false → loginFirstTimeDriver │ │ وإلا → loginJwtDriver │ │ يحفظ JWT في box + secure storage │ └────────────────────────────────────────────────────────────────────┘ ``` --- ## 4. تحليل تدفق البيانات — من وإلى التطبيق ### 📤 من التطبيق إلى الباك إند | المعلومة | هل التنظيف صحيح؟ | الطريقة | |----------|-------------------|---------| | email | ✅ | `filterRequest('email')` ثم `encryptData()` قبل الاستعلام | | phone | ✅ | `filterRequest('phone')` ثم `encryptData()` قبل الاستعلام | | password | ✅ | يُستخدم `password_verify()` مع `password_hash` في DB | | fingerprint | ✅ | يُهش مع Pepper: `SHA-256(fingerprint + pepper)` | | driverID | ✅ | `filterRequest('id')` | | token / otp | ⚠️ | `token_code` يُرسل بدون تشفير في `/verifyOtpDriver` | ### 📥 من الباك إند إلى التطبيق | المعلومة | هل التسريب آمن؟ | ملاحظة | |----------|-----------------|---------| | JWT | ✅ | يُخزَّن في `GetStorage` + `FlutterSecureStorage` | | HMAC | ✅ | يُستخدم للتوثيق بين الطلبات | | الاسم (first_name, last_name) | ✅ | يُفك تشفيره للعرض فقط | | الهاتف والإيميل | ✅ | يُفك تشفيرهما للعرض فقط | | make, model, year | ✅ | غير حساسة، لا تشفير | | bankCode, accountBank | ⚠️ | حساسة لكنها تُفك تشفيرها وتُرسل | --- ## 5. النقاط الأمنية والثغرات ### ✅ نقاط القوة 1. **Rate Limiting** في loginJwtDriver.php و loginFirstTimeDriver.php ✅ 2. **JWT مع Fingerprint و Pepper** — ربط التوكن بالجهاز ✅ 3. **HMAC للمصادقة الداخلية** — مشتق من id\|phone\|national_number ✅ 4. **إلغاء التوكن القديم** عبر Redis قبل إصدار جديد ✅ 5. **تشفير PII في قاعدة البيانات** — encryptData لكل الحقول الحساسة ✅ 6. **password_hash + password_verify** في كل نقاط التحقق ✅ 7. **Audience Validation** — التحقق من الـ audience المسموح ✅ 8. **NetGuard في CRUD** — التحقق من SSL قبل الاتصال ✅ 9. **فصل التوكنات** — registration token (450s) ≠ access token (4h) ✅ 10. **فحص صلاحية JWT** — `_isJwtValid()` قبل أي طلب ✅ ### ❌ الثغرات والملاحظات | # | الثغرة | الموقع | التأثير | التصنيف | |---|--------|--------|---------|---------| | 1 | **loginFromGoogle.php يقرأ connect.php** (قديم، لا JWT) | `backend/auth/captin/loginFromGoogle.php` | لا يوجد JWT Authentication — يمكن لأي جهة خارجية استدعاء API وسحب بيانات السائق | 🔴 **Critical** | | 2 | **loginUsingCredentialsWithoutGoogle.php يقرأ connect.php** (قديم) | `backend/auth/captin/loginUsingCredentialsWithoutGoogle.php` | لا يوجد JWT Authentication — يمكن اختراق كلمة المرور بقوة عمياء دون Rate Limiting | 🔴 **Critical** | | 3 | **login.php (auth/captin) لا يستخدم connect.php الجديد** | `backend/auth/captin/login.php` | لا Rate Limiting ولا JWT Authentication | 🔴 **Critical** | | 4 | **is_verified = '1' إجباري في loginUsingCredentials** | `loginUsingCredentialsWithoutGoogle.php` سطر 38 | السائق يحتاج التحقق من الهاتف حتى يتمكن من تسجيل الدخول (قد يكون مقصوداً لكنه قاسٍ على المستخدم) | 🟡 **Medium** | | 5 | **connect.php القديم** | `backend/connect.php` نفسه | لا Rate Limiting ولا JWT — أي API يستخدم connect.php فقط مكشوف للجميع | 🔴 **Critical** | | 6 | **loginFromGoogle.php لا يتحقق من البريد (معلق)** | سطر 10-18 | البحث فقط بـ `driverID` — أي driverID صحيح يعيد البيانات حتى لو الإيميل مختلف | 🟡 **Medium** | | 7 | **OTP code يرسل إلى الباك إند نصاً بدون تشفير** | `register_captin_controller.dart` سطر 266 | `token_code` يُرسل كـ plain text | 🟡 **Medium** | | 8 | **Secure Storage vs GetStorage** | `login_captin_controller.dart` | JWT يُخزَّن في **كلاهما** — GetStorage غير مشفر (plain text على القرص) | 🔴 **Critical** | | 9 | **password مخزَّن في متغير بيئة (passwordnewpassenger)** | `loginFirstTimeDriver.php` سطر 30 | كلمة مرور عامة لكل السائقين للتسجيل الأولي — إذا سُرّبت، يمكن توليد registration tokens وهمية | 🔴 **Critical** | | 10 | **لا Validation على الـ id في loginJwtDriver** | `loginJwtDriver.php` | إذا تم إرسال id غير موجود، يعيد "invalid credentials" (وهو صحيح لكن يمكن استغلاله في تحديد IDs) | 🟢 **Low** | --- ## 6. مخطط الـ OTP — كامل ``` ┌───────────────────┐ ┌─────────────────────┐ │ Flutter │ │ PHP Backend │ ├───────────────────┤ ├─────────────────────┤ │ │ │ │ │ sendOtpMessage() │ │ │ │ │ │ │ │ │ ├── checkPhoneNumberISVerfiedDriver │ │ │ POST ──────┼────────>│ التحقق إذا الرقم │ │ │ │ │ موثَّق مسبقاً │ │ │ ← is_verified=1 ──────┤ │ │ │ ← already verified → loginWithGoogleCredential│ │ │ │ │ │ │ ├── sendVerifyOtpMessage │ │ │ │ POST: {phone_number, driverId, email} │ │ │ ───────────┼────────>│ إرسال OTP عبر SMS │ │ │ │ │ + حفظ في DB │ │ │ │ │ │ │ ├── verifyOtpDriver (verifySMSCode) │ │ │ POST: {phone_number, token_code} │ │ │ ───────────┼────────>│ التحقق من OTP │ │ │ │ │ │ │ │ ← success ───────────│ │ │ │ phoneVerified=1 │ │ │ │ ├── تخزين phoneDriver │ │ │ │ └── Get.to(RegistrationView) │ │ │ │ │ └───────────────────┘ └─────────────────────┘ ``` ### الثغرة في تدفق OTP: عندما يكون `is_verified=1`، ينتقل التطبيق **مباشرةً** إلى `loginWithGoogleCredential()` دون إعادة إدخال كلمة المرور أو أي تحقق إضافي. هذا يعني أنه إذا تم الوصول إلى جهاز المستخدم مؤقتاً، يمكن تسجيل الدخول فقط بالتحقق من أن الرقم "موثَّق مسبقاً". --- ## 7. الملخص النهائي | البند | النتيجة | |-------|---------| | **إجمالي الملفات المدققة** | 22 ملفاً (Flutter + PHP) | | **الثغرات الحرجة (Critical)** | 5 | | **الثغرات المتوسطة (Medium)** | 3 | | **الثغرات المنخفضة (Low)** | 2 | | **النقاط الإيجابية** | 10 | ### الثغرات الحرجة (Critical) التي تتطلب إصلاحاً فورياً: 1. **🔴 loginFromGoogle.php** ← يستخدم connect.php القديم (لا JWT, لا Rate Limiting) 2. **🔴 loginUsingCredentialsWithoutGoogle.php** ← يستخدم connect.php القديم 3. **🔴 auth/captin/login.php** ← لا Rate Limiting ولا JWT 4. **🔴 JWT مخزَّن في GetStorage (غير مشفر)** — يجب استخدام FlutterSecureStorage فقط 5. **🔴 passwordnewpassenger عام لكل السائقين في loginFirstTimeDriver.php** ### هل التدفق صحيح؟ - **من التطبيق إلى الباك إند**: ✅ صحيح — `filterRequest()` ينظف المدخلات - **التشفير في DB**: ✅ صحيح — `encryptData()` لكل الحقول الحساسة - **التحقق من كلمة المرور**: ✅ صحيح — `password_hash()` + `password_verify()` - **Fingerprint + Pepper**: ✅ صحيح — يربط التوكن بالجهاز - **إدارة التوكنات**: ✅ صحيح — Registration ثم Access مع Revocation - **المصادقة (Authentication)**: ❌ **ضعيف** — الملفات القديمة (connect.php) لا تتطلب JWT - **تخزين البيانات الحساسة**: ❌ **ضعيف** — GetStorage غير مشفر على Android ### التوصية: يجب تحويل `loginFromGoogle.php` و `loginUsingCredentialsWithoutGoogle.php` لاستخدام `core/bootstrap.php` مع JWT Authentication بدلاً من `connect.php` القديم، وتوحيد GetStorage إلى FlutterSecureStorage للـ JWT. --- > **تاريخ التحليل:** 12 يونيو 2026 > **النسخة:** 1.0 > **التركيز:** Siro Driver فقط