Files
Siro/REMEDIATION_GUIDE.md
2026-06-16 17:47:19 +03:00

602 lines
17 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# دليل الإصلاحات الشامل - مشروع سيرو
**التاريخ:** 16 يونيو 2026
**المرحلة:** التطبيق العملي للإصلاحات
**الحالة:** قيد التطوير
---
## النقطة 1⃣: مشكلة البصمة الضعيفة (Weak Fingerprint Authentication)
### الملفات المتأثرة:
- `backend/login.php` (الراكب)
- `backend/loginJwtDriver.php` (السائق)
### المشكلة الحقيقية:
```php
// ❌ الحالة الحالية في login.php
$fpVerified = hash_equals($storedFp, $fingerprint);
// ✅ يحمي من timing attacks فقط
// ⚠️ لكن البصمة نفسها ضعيفة وقابلة للاستخراج
```
**لماذا ضعيفة؟**
- البصمة مستخرجة من جهاز المستخدم (ANDROID_ID + IMEI + MAC + Build.MODEL)
- يمكن استخراجها عبر:
- Frida/Objection أثناء التطبيق
- ADB إذا كان USB debugging مفعل
- مفاتيح الجهاز المُخزنة
- بمجرد استخراج البصمة، يمكن تزويرها بنسخ نفس القيمة
### الحل: تطبيق MFA (Multi-Factor Authentication)
**المتطلبات:**
```
عامل 1: بصمة الجهاز (الحالي) ✅
عامل 2: OTP عبر SMS ← أضف هذا
عامل 3: رمز الخادم (Server Token) ← أضف هذا
```
---
## النقطة 2⃣: مشكلة التشفير - IV الثابت
### الملف المتأثر:
- `backend/encrypt_decrypt.php` (الأساسي)
### المشكلة الفعلية:
```php
// ❌ المشكلة الحرجة جداً
$iv = getenv('initializationVector'); // 16 بايت ثابت!
public function encryptData($plainText) {
$plainText = mb_convert_encoding($plainText, 'UTF-8');
$paddedText = $this->addPadding($plainText);
$encrypted = openssl_encrypt($paddedText, 'AES-256-CBC',
$this->key, OPENSSL_RAW_DATA, $this->iv);
// ↑ نفس IV في كل مرة = نفس ciphertext لنفس plaintext!
return base64_encode($encrypted);
}
```
**مثال عملي:**
```
التشفير الأول: "+20123456789" → "abc123xyz=="
التشفير الثاني: "+20123456789" → "abc123xyz==" (متطابق!)
⚠️ هجوم معروف - يمكن كسر التشفير تماماً
```
### الحل: توليد IV عشوائي
```php
الحل الصحيح:
public function encryptData($plainText) {
$plainText = mb_convert_encoding($plainText, 'UTF-8');
$paddedText = $this->addPadding($plainText);
// توليد IV عشوائي لكل تشفير
$randomIV = openssl_random_pseudo_bytes(16);
$encrypted = openssl_encrypt($paddedText, 'AES-256-CBC',
$this->key, OPENSSL_RAW_DATA, $randomIV);
// ضمّ IV مع النص المشفر
$result = $randomIV . $encrypted;
return base64_encode($result);
}
public function decryptData($encryptedData) {
$encrypted = base64_decode($encryptedData);
// استخرج IV من أول 16 بايت
$iv = substr($encrypted, 0, 16);
$ciphertext = substr($encrypted, 16);
$decrypted = openssl_decrypt($ciphertext, 'AES-256-CBC',
$this->key, OPENSSL_RAW_DATA, $iv);
return $this->removePadding($decrypted);
}
```
**الخطوات:**
1. توليد 16 بايت عشوائية → `openssl_random_pseudo_bytes(16)`
2. تشفير البيانات بـ IV العشوائي
3. ضمّ IV + Ciphertext
4. تحويل إلى base64
5. عند فك التشفير: استخرج IV + Ciphertext وفك التشفير
---
## النقطة 3⃣: SQL Injection - الحالة الحالية وضح!
### الملف المتأثر:
- `backend/functions.php` (في `findBestDrivers()`)
### الحالة الحالية - ممتازة ✅
```php
// ✅ هذا الكود **آمن تماماً** من SQL Injection
$carType = trim($carType);
$allowedCarTypes = [
'Comfort', 'Mishwar Vip', 'Scooter', 'Pink Bike',
'Electric', 'Lady', 'Van', 'Awfar Car', 'Fixed Price',
'Speed', 'Rayeh Gai'
];
// ✅ استخدام Allowlist (أفضل طريقة)
if (!in_array($carType, $allowedCarTypes, true)) {
$carType = 'Speed';
}
// ✅ معاملات SQL آمنة
$stmt = $con->prepare($sql);
$stmt->execute($allParams);
```
### لماذا هذا آمن؟
1. **Allowlist:** قائمة بيضاء للقيم المسموحة فقط
2. **Prepared Statements:** معاملات آمنة
3. **Type Strict (`true`):** التحقق الدقيق (`in_array(..., true)`)
### المشاكل المتبقية:
**نقاط أخرى قد تحتاج فحص:**
- `/backend/auth/` - هل تستخدم prepared statements؟
- `/backend/ride/` - هل جميع الاستعلامات آمنة؟
- `/walletintaleq.intaleq.xyz/v2/main/` - **حرج! تحتاج فحص كامل**
---
## النقطة 4⃣: نظام المحفظة - أهم نقطة 🔴
### الملفات المتأثرة:
```
/walletintaleq.intaleq.xyz/v2/main/ride/driverWallet/
├── add.php ..................... 🔴 حرج - بلا مصادقة
├── transfer.php ............... ⚠️ عالي
├── update.php ................. ⚠️ عالي
├── addFromAdmin.php ........... 🔴 حرج - مفتاح API ثابت
├── get.php .................... ⚠️ عالي
├── getWalletByDriver.php ...... ⚠️ عالي
└── getDriverDetails.php ....... ⚠️ عالي
```
### الفهم الحالي:
```
تطبيق السائق
POST /add ← يضيف الأموال
/walletintaleq.intaleq.xyz/v2/main/ride/driverWallet/add.php
← لا يوجد مصادقة! ❌
```
### الحل المقترح: (S2S - Server to Server)
```
تطبيق السائق
POST /wallet/add (مع JWT توكن)
BACKEND SERVER (backend/wallet/) ← نقطة جديدة
• التحقق من JWT ✅
• فحص الملكية ✅
• التحقق من المبلغ ✅
POST /v2/main/ride/driverWallet/add
(مع توقيع HMAC-SHA256)
WalletIntaleq Server
• التحقق من التوقيع ✅
• تنفيذ العملية ✅
• تسجيل دقيق ✅
```
### الخطوات العملية:
#### 1⃣ إنشاء endpoint في Backend
```php
// backend/wallet/add.php (جديد)
require_once __DIR__ . '/../core/bootstrap.php';
function requireAuth() {
$headers = getallheaders();
$authHeader = $headers['Authorization'] ?? '';
if (!preg_match('/Bearer\s+(\S+)/', $authHeader, $matches)) {
http_response_code(401);
jsonError('Authentication required');
}
$token = $matches[1];
$jwtService = new JwtService($redis);
try {
$decoded = $jwtService->verifyAccessToken($token);
return $decoded;
} catch (Exception $e) {
http_response_code(401);
jsonError('Invalid token');
}
}
try {
$user = requireAuth(); // JWT verification
// التحقق من الدور (التفويض)
if ($user->role !== 'driver') {
http_response_code(403);
jsonError('Permission denied');
}
$driverID = $user->id;
$amount = floatval(filterRequest('amount'));
$source = filterRequest('source');
// التحقق من المبلغ
if ($amount <= 0 || $amount > 10000) {
jsonError('Invalid amount (max 10,000)', 400);
}
// تحديد السرعة
$limiter = new RateLimiter($redis);
$limiter->enforce("wallet_add_{$driverID}", 'add', 1, 60); // 1 request per 60 seconds
// تسجيل التدقيق
$auditLog = "Driver {$driverID} requested to add {$amount} from {$source}";
securityLog($auditLog);
// الاتصال بـ WalletIntaleq عبر S2S
$walletResponse = callWalletAPI('add', [
'driver_id' => $driverID,
'amount' => $amount,
'source' => $source,
'backend_request' => true,
]);
if ($walletResponse['status'] === 'success') {
jsonSuccess(['message' => 'تمت إضافة الأموال بنجاح']);
} else {
jsonError('Wallet operation failed', 500);
}
} catch (Exception $e) {
securityLog("Wallet add error: " . $e->getMessage());
jsonError('Internal error', 500);
}
```
#### 2⃣ دالة S2S API call آمنة
```php
// backend/core/WalletConnector.php (جديد)
class WalletConnector {
private $walletUrl = 'https://walletintaleq.intaleq.xyz/v2/main/';
private $hmacSecret = null;
public function __construct() {
$this->hmacSecret = getenv('WALLET_HMAC_SECRET');
if (!$this->hmacSecret) {
throw new Exception("WALLET_HMAC_SECRET not configured");
}
}
public function call($endpoint, $data) {
// إضافة timestamp لمنع Replay Attacks
$data['timestamp'] = time();
$data['nonce'] = bin2hex(random_bytes(16));
// فرز البيانات وإنشاء signature
ksort($data);
$payload = json_encode($data);
$signature = hash_hmac('sha256', $payload, $this->hmacSecret);
// إرسال الطلب
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $this->walletUrl . $endpoint,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-Signature: ' . $signature,
'X-Timestamp: ' . $data['timestamp'],
'X-Backend-ID: ' . getenv('BACKEND_ID'),
],
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_TIMEOUT => 10,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// التحقق من الاستجابة
$decoded = json_decode($response, true);
if ($httpCode !== 200 || $decoded['status'] !== 'success') {
throw new Exception("Wallet API error: " . $response);
}
return $decoded;
}
}
```
#### 3⃣ تعديل WalletIntaleq
```php
// walletintaleq.intaleq.xyz/v2/main/ride/driverWallet/add.php
<?php
// ✅ الآن يتلقى طلبات من Backend فقط
require_once __DIR__ . '/../../../core/bootstrap.php';
header('Content-Type: application/json');
try {
// الحصول على البيانات المرسلة
$payload = file_get_contents('php://input');
$data = json_decode($payload, true);
// التحقق من التوقيع (HMAC)
$signature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
$expectedSignature = hash_hmac('sha256', $payload, getenv('WALLET_HMAC_SECRET'));
if (!hash_equals($signature, $expectedSignature)) {
http_response_code(401);
jsonError('Invalid signature');
}
// التحقق من الـ Timestamp (Replay Attack Prevention)
$timestamp = $_SERVER['HTTP_X_TIMESTAMP'] ?? 0;
if (abs(time() - $timestamp) > 300) { // 5 دقائق
http_response_code(401);
jsonError('Request expired');
}
// الآن آمن - نفذ العملية
$driverID = $data['driver_id'];
$amount = $data['amount'];
$source = $data['source'];
// أضف إلى قاعدة البيانات
$stmt = $con->prepare(
"INSERT INTO driverWallet (driver_id, amount, source, created_at, updated_at)
VALUES (?, ?, ?, NOW(), NOW())"
);
$stmt->execute([$driverID, $amount, $source]);
// تسجيل التدقيق
securityLog("Wallet added: Driver $driverID, Amount $amount from $source");
jsonSuccess(['message' => 'Added successfully']);
} catch (Exception $e) {
securityLog("Wallet error: " . $e->getMessage());
jsonError('Internal error', 500);
}
?>
```
---
## النقطة 5⃣: أمان تطبيقات الهاتف - الأذونات
### الملف المتأثر:
- `siro_driver/android/app/src/main/AndroidManifest.xml`
### الأذونات الحالية:
```xml
<!-- ✅ ضرورية لتطبيق السائق -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<!-- ⚠️ تحتاج فحص -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<!-- ✅ ضرورية للاتصالات -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
```
### الفحص المطلوب:
```bash
1. بحث في codebase تطبيق السائق:
grep -r "getExternalFilesDir\|getExternalCacheDir\|Environment.getExternalStorageDirectory" .
إذا لم تظهر نتائج → احذف WRITE/READ_EXTERNAL_STORAGE
2. بحث عن SYSTEM_ALERT_WINDOW:
grep -r "TYPE_APPLICATION_OVERLAY\|canDrawOverlays" .
إذا لم تظهر → احذفها
3. بحث عن MODIFY_AUDIO_SETTINGS:
grep -r "setVolume\|adjustStreamVolume" .
إذا لم تظهر → احذفها
```
---
## النقطة 6⃣: load_env.php - تحميل آمن للمتغيرات
### الملف:
- `backend/load_env.php`
### الحالة الحالية:
```php
<?php
function loadEnvironment($env_file) {
if (!file_exists($env_file)) {
error_log("❌ .env not found: $env_file");
return false;
}
$lines = file($env_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
$line = trim($line);
if (empty($line) || strpos($line, '#') === 0) continue;
$parts = explode('=', $line, 2);
if (count($parts) === 2) {
[$keyName, $value] = $parts;
$value = trim($value, "\"'");
putenv("$keyName=$value");
$_ENV[$keyName] = $value;
$_SERVER[$keyName] = $value;
}
}
return true;
}
```
### القوة الأمنية:
**جيد جداً:**
1. يتحقق من وجود الملف
2. يتجاهل التعليقات (#)
3. يتجاهل الأسطر الفارغة
4. يزيل علامات الاقتباس
5. يحمل في `$_ENV` و `$_SERVER`
### الطريقة الموصى بها (Composer autoload):
```json
// composer.json - الأفضل
"autoload": {
"files": ["src/bootstrap.php"]
}
```
```php
// src/bootstrap.php
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
```
---
## النقطة 7⃣: backend/functions.php - الروابط الآمنة
### الحالة الحالية:
```php
غير آمنة:
$url = "http://188.68.36.205:2021";
$url = "http://188.68.36.205:3031";
$url = "https://location.intaleq.xyz";
```
### المشاكل:
1. **IP عام مكشوف** - 188.68.36.205
2. **HTTP بدل HTTPS** - عرضة لـ MITM attacks
3. **لا يوجد certificate pinning**
### الحل:
```php
// ✅ الحل الآمن
function getAllowedSocketUrls(): array {
return [
'https://location.siromove.com', // ✅ HTTPS + Domain
'https://socket.siromove.com', // ✅ HTTPS + Domain
// لا HTTP
];
}
function sendToLocationServer($action, $data) {
$url = "https://location.siromove.com/api";
// تثبيت الشهادة (Certificate Pinning)
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => http_build_query($data),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
// ✅ تأمين SSL/TLS
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_CAINFO => '/etc/ssl/certs/ca-bundle.crt',
// ✅ Certificate Pinning (اختياري لكن موصى)
// تحتاج حساب SHA-256 للشهادة
CURLOPT_PINNEDPUBLICKEY => 'sha256/AAAA....',
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
```
---
## ملخص الإصلاحات
| النقطة | المشكلة | الحل | المدة |
| ------ | ------------------ | ------------- | -------- |
| 1 | بصمة ضعيفة | MFA + OTP | 8 ساعات |
| 2 | IV ثابت | توليد عشوائي | 4 ساعات |
| 3 | SQL Injection | آمن بالفعل ✅ | 0 ساعات |
| 4 | المحفظة بلا مصادقة | S2S + JWT | 16 ساعات |
| 5 | أذونات مفرطة | تقليص + فحص | 4 ساعات |
| 6 | load_env.php | آمن بالفعل ✅ | 0 ساعات |
| 7 | روابط HTTP | تبديل HTTPS | 2 ساعات |
**المجموع:** ~34 ساعة
---
## التالي:
1. تطبيق الإصلاحات واحدة تلو الأخرى
2. اختبار كل إصلاح
3. ترحيل قاعدة البيانات
4. نشر للإنتاج