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

515 lines
16 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⃣: الإجراءات الفورية (اليوم الأول - 4 ساعات)
### ✅ الخطوة 1: تعطيل نقاط نهاية المحفظة الخطيرة
**السبب:** منع الاحتيال المالي الفوري
```bash
# نسخ احتياطية أولاً
cp /walletintaleq.intaleq.xyz/v2/main/ride/driverWallet/add.php \
/walletintaleq.intaleq.xyz/v2/main/ride/driverWallet/add.php.bak
cp /walletintaleq.intaleq.xyz/v2/main/ride/driverWallet/addFromAdmin.php \
/walletintaleq.intaleq.xyz/v2/main/ride/driverWallet/addFromAdmin.php.bak
```
**التعطيل المؤقت:**
```php
<?php
// /walletintaleq.intaleq.xyz/v2/main/ride/driverWallet/add.php
// ⚠️ معطل مؤقتاً - يتم إصلاح الأمان
http_response_code(503);
echo json_encode([
'status' => 'error',
'message' => 'Wallet service temporarily disabled for security updates',
'eta' => '2026-06-17 00:00:00 UTC'
]);
exit;
?>
```
---
### ✅ الخطوة 2: إنشاء endpoint جديد في Backend
**الموقع:** `backend/wallet/add.php` (جديد)
```php
<?php
// backend/wallet/add.php - ✅ جديد وآمن
require_once __DIR__ . '/../core/bootstrap.php';
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: https://siromove.com');
header('Access-Control-Allow-Methods: POST');
try {
// 1⃣ مصادقة JWT إجبارية
$headers = getallheaders();
$authHeader = $headers['Authorization'] ?? '';
if (!preg_match('/Bearer\s+(\S+)/', $authHeader, $matches)) {
http_response_code(401);
jsonError('Authentication required (JWT needed)', 401);
}
// التحقق من JWT
$jwtService = new JwtService($redis);
try {
$decoded = $jwtService->verifyAccessToken($matches[1]);
} catch (Exception $e) {
http_response_code(401);
jsonError('Invalid or expired token', 401);
}
// 2⃣ التحقق من الدور (التفويض)
if ($decoded->role !== 'driver') {
http_response_code(403);
jsonError('Only drivers can add funds', 403);
}
$driverID = (int) $decoded->id;
$amount = (float) filterRequest('amount');
$source = filterRequest('source');
// 3⃣ تحديد السرعة
$limiter = new RateLimiter($redis);
try {
$limiter->enforce("wallet_add_" . $driverID, 'add');
} catch (Exception $e) {
http_response_code(429);
jsonError('Too many requests. Please try again later.', 429);
}
// 4⃣ التحقق من المبلغ
if ($amount <= 0) {
jsonError('Amount must be greater than 0', 400);
}
if ($amount > 10000) {
jsonError('Amount cannot exceed 10,000', 400);
}
// 5⃣ تسجيل التدقيق
securityLog("Driver wallet add request", [
'driver_id' => $driverID,
'amount' => $amount,
'source' => $source,
'timestamp' => date('Y-m-d H:i:s')
]);
// 6⃣ حفظ في قاعدة البيانات المحلية أولاً
$con = Database::get('main');
$stmt = $con->prepare(
"INSERT INTO driver_wallet_requests (driver_id, amount, source, status)
VALUES (?, ?, ?, 'pending')"
);
$stmt->execute([$driverID, $amount, $source]);
$requestID = $con->lastInsertId();
// 7⃣ استدعاء خادم المحفظة عبر S2S API
$walletConnector = new WalletConnector();
try {
$response = $walletConnector->call('ride/driverWallet/add_s2s', [
'driver_id' => $driverID,
'amount' => $amount,
'source' => $source,
'request_id' => $requestID,
'backend_id' => getenv('BACKEND_ID'),
]);
// ✅ النجاح
$stmt = $con->prepare(
"UPDATE driver_wallet_requests SET status = 'completed' WHERE id = ?"
);
$stmt->execute([$requestID]);
jsonSuccess(['message' => 'Funds added successfully']);
} catch (Exception $e) {
// فشل العملية - سجل الخطأ
$stmt = $con->prepare(
"UPDATE driver_wallet_requests SET status = 'failed', error = ? WHERE id = ?"
);
$stmt->execute([$e->getMessage(), $requestID]);
securityLog("Wallet S2S call failed", [
'driver_id' => $driverID,
'error' => $e->getMessage()
]);
http_response_code(500);
jsonError('Failed to process payment', 500);
}
} catch (Exception $e) {
securityLog("Wallet endpoint error: " . $e->getMessage());
http_response_code(500);
jsonError('Internal server error', 500);
}
?>
```
---
### ✅ الخطوة 3: إنشاء فئة WalletConnector آمنة
**الموقع:** `backend/core/WalletConnector.php` (جديد)
```php
<?php
// backend/core/WalletConnector.php - ✅ جديد وآمن
class WalletConnector {
private $walletUrl;
private $hmacSecret;
private $backendID;
private $timeout = 10;
public function __construct() {
$this->walletUrl = getenv('WALLET_API_URL') ?? 'https://walletintaleq.intaleq.xyz/v2/main/';
$this->hmacSecret = getenv('WALLET_HMAC_SECRET');
$this->backendID = getenv('BACKEND_ID');
if (!$this->walletUrl || !$this->hmacSecret || !$this->backendID) {
throw new Exception("Missing wallet configuration");
}
}
/**
* استدعاء API خادم المحفظة بأمان (S2S)
*/
public function call($endpoint, $data) {
// ✅ أضف timestamp ونonce لمنع Replay Attacks
$data['timestamp'] = time();
$data['nonce'] = bin2hex(random_bytes(16));
$data['backend_id'] = $this->backendID;
// ✅ فرز البيانات
ksort($data);
// ✅ إنشاء JSON payload
$payload = json_encode($data);
// ✅ إنشاء توقيع HMAC
$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_TIMEOUT => $this->timeout,
// ✅ رؤوس HTTP آمنة
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-Signature: ' . $signature,
'X-Timestamp: ' . $data['timestamp'],
'X-Backend-ID: ' . $this->backendID,
'User-Agent: SiroBackend/1.0',
],
// ✅ تأمين SSL/TLS
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_CAINFO => '/etc/ssl/certs/ca-bundle.crt',
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
// ✅ فحص الأخطاء
if ($curlError) {
throw new Exception("CURL Error: $curlError");
}
// ✅ فحص رمز HTTP
if ($httpCode !== 200) {
throw new Exception("Wallet API returned HTTP $httpCode: $response");
}
// ✅ فحص الاستجابة
$decoded = json_decode($response, true);
if ($decoded === null) {
throw new Exception("Invalid JSON response from wallet");
}
if ($decoded['status'] !== 'success') {
throw new Exception("Wallet error: " . ($decoded['message'] ?? 'Unknown'));
}
return $decoded;
}
}
?>
```
---
### ✅ الخطوة 4: تعديل طريقة الترحيل
**قاعدة البيانات الجديدة:**
```sql
-- جديد: جدول لتتبع طلبات إضافة الأموال
CREATE TABLE driver_wallet_requests (
id INT AUTO_INCREMENT PRIMARY KEY,
driver_id INT NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
source VARCHAR(50),
status ENUM('pending', 'completed', 'failed') DEFAULT 'pending',
error TEXT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (driver_id) REFERENCES driver(id),
INDEX (driver_id, status),
INDEX (created_at)
);
-- ملف .env جديد (اضف هذه المتغيرات)
# wallet configuration
WALLET_API_URL=https://walletintaleq.intaleq.xyz/v2/main/
WALLET_HMAC_SECRET=<secret-key-here-change-me>
BACKEND_ID=siromove-backend-01
```
---
## المرحلة 2⃣: إصلاح التشفير (اليوم الثاني - 4 ساعات)
### ✅ الخطوة 5: تعديل encrypt_decrypt.php
**الملف:** `backend/encrypt_decrypt.php`
سأنشئ نسخة محدثة آمنة:
```php
<?php
// backend/encrypt_decrypt.php - ✅ تم تحديثه
class EncryptionHelper {
private $key;
public function __construct($key = null) {
if ($key === null) {
$key = getenv('ENC_KEY');
if (!$key) {
$keyPath = getenv('ENCRYPTION_KEY_PATH');
if ($keyPath && file_exists($keyPath)) {
$key = trim(file_get_contents($keyPath));
}
}
}
if (strlen($key) !== 32) {
throw new Exception("Key must be exactly 32 bytes (256 bits) for AES-256");
}
$this->key = $key;
}
// ✅ ضمّ IV مع النص المشفر قبل base64_encode
private function addPadding($data, $blockSize = 16) {
$pad = $blockSize - (strlen($data) % $blockSize);
return $data . str_repeat(chr($pad), $pad);
}
private function removePadding($data) {
$pad = ord($data[strlen($data) - 1]);
if ($pad < 1 || $pad > 16) {
throw new Exception("Invalid padding");
}
return substr($data, 0, -$pad);
}
/**
* تشفير البيانات بـ IV عشوائي
* ✅ التحسين: IV عشوائي لكل تشفير
*/
public function encryptData($plainText) {
try {
$plainText = mb_convert_encoding($plainText, 'UTF-8');
$paddedText = $this->addPadding($plainText);
// ✅ توليد IV عشوائي - هذا هو الإصلاح الحرج
$randomIV = openssl_random_pseudo_bytes(16, $strongRandom);
if (!$strongRandom) {
throw new Exception("openssl_random_pseudo_bytes failed to generate strong random");
}
// تشفير البيانات
$encrypted = openssl_encrypt(
$paddedText,
'AES-256-CBC',
$this->key,
OPENSSL_RAW_DATA,
$randomIV
);
if (!$encrypted) {
throw new Exception("Encryption failed");
}
// ✅ ضمّ IV مع النص المشفر (IV يجب أن يكون أول 16 بايت)
$encryptedWithIV = $randomIV . $encrypted;
// تحويل إلى base64
return base64_encode($encryptedWithIV);
} catch (Exception $e) {
error_log("Encryption error: " . $e->getMessage());
throw $e;
}
}
/**
* فك التشفير - استخرج IV من البيانات المشفرة
* ✅ التحسين: قراءة IV من البيانات المشفرة
*/
public function decryptData($encryptedData) {
try {
// فك base64
$encrypted = base64_decode($encryptedData, true);
if (!$encrypted) {
throw new Exception("Invalid base64 data");
}
if (strlen($encrypted) < 16) {
throw new Exception("Encrypted data too short");
}
// ✅ استخرج IV من أول 16 بايت
$iv = substr($encrypted, 0, 16);
$ciphertext = substr($encrypted, 16);
// فك التشفير
$decrypted = openssl_decrypt(
$ciphertext,
'AES-256-CBC',
$this->key,
OPENSSL_RAW_DATA,
$iv
);
if (!$decrypted) {
throw new Exception("Decryption failed");
}
// إزالة الـ padding
$unpadded = $this->removePadding($decrypted);
return $unpadded;
} catch (Exception $e) {
error_log("Decryption error: " . $e->getMessage());
throw $e;
}
}
}
// استخدام
$encryption = new EncryptionHelper();
// ✅ التشفير
$plaintext = "+20123456789";
$encrypted1 = $encryption->encryptData($plaintext);
$encrypted2 = $encryption->encryptData($plaintext);
echo "Encryption 1: $encrypted1\n";
echo "Encryption 2: $encrypted2\n";
echo "Different? " . ($encrypted1 !== $encrypted2 ? "YES ✅" : "NO ❌") . "\n";
// ✅ فك التشفير
$decrypted = $encryption->decryptData($encrypted1);
echo "Decrypted: $decrypted\n";
echo "Correct? " . ($decrypted === $plaintext ? "YES ✅" : "NO ❌") . "\n";
?>
```
---
## المرحلة 3⃣: تقليل الأذونات (ساعة واحدة)
### ✅ الخطوة 6: تحديث AndroidManifest.xml
```xml
<!-- قبل -->
<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" />
<!-- بعد: احذف هذه الأسطر الأربعة ↑ -->
```
**السبب:**
- ❌ لا يوجد استخدام فعلي لـ External Storage
- ❌ لا يوجد استخدام لـ SYSTEM_ALERT_WINDOW
- ⚠️ MODIFY_AUDIO_SETTINGS يمكن استبداله بـ requestAudioFocus
---
## المرحلة 4⃣: تحديث الروابط (30 دقيقة)
### ✅ الخطوة 7: تحديث functions.php
في `backend/functions.php`:
```php
// ❌ احذف هذه:
function getAllowedSocketUrls(): array {
return [
'http://188.68.36.205:2021',
'http://188.68.36.205:3031',
'https://location.intaleq.xyz',
];
}
// ✅ استبدل بهذا:
function getAllowedSocketUrls(): array {
$urls = getenv('ALLOWED_SOCKET_URLS');
if ($urls) {
return array_map('trim', explode(',', $urls));
}
return [
'https://location.siromove.com', // HTTPS فقط
'https://socket.siromove.com', // HTTPS فقط
];
}
```
**في .env:**
```
# Socket URLs - HTTPS only
ALLOWED_SOCKET_URLS=https://location.siromove.com,https://socket.siromove.com
```
---
## ✅ الخلاصة والجدول الزمني
| المرحلة | الخطوات | المدة | التاريخ المتوقع |
| ------------ | ------------------------------------------ | ------------- | --------------- |
| 1 | تعطيل + Backend endpoint + WalletConnector | 4 س | اليوم (16/6) |
| 2 | تحديث encrypt_decrypt.php + اختبار | 4 س | غد (17/6) |
| 3 | تقليل الأذونات + نشر تطبيق | 1 س | غد (17/6) |
| 4 | تحديث الروابط + اختبار | 30 د | غد (17/6) |
| **الإجمالي** | - | **~9.5 ساعة** | **بحلول 17/6** |