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

17 KiB
Raw Blame History

دليل الإصلاحات الشامل - مشروع سيرو

التاريخ: 16 يونيو 2026
المرحلة: التطبيق العملي للإصلاحات
الحالة: قيد التطوير


النقطة 1: مشكلة البصمة الضعيفة (Weak Fingerprint Authentication)

الملفات المتأثرة:

  • backend/login.php (الراكب)
  • backend/loginJwtDriver.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 (الأساسي)

المشكلة الفعلية:

// ❌ المشكلة الحرجة جداً
$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 عشوائي

 الحل الصحيح:

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())

الحالة الحالية - ممتازة

// ✅ هذا الكود **آمن تماماً** من 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

// 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 آمنة

// 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

// 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

الأذونات الحالية:

<!-- ✅ ضرورية لتطبيق السائق -->
<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" />

الفحص المطلوب:

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
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):

// composer.json - الأفضل

"autoload": {
    "files": ["src/bootstrap.php"]
}
// src/bootstrap.php

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

النقطة 7: backend/functions.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

الحل:

// ✅ الحل الآمن

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. نشر للإنتاج