Update: 2026-06-16 17:47:17
This commit is contained in:
514
IMPLEMENTATION_STEPS.md
Normal file
514
IMPLEMENTATION_STEPS.md
Normal file
@@ -0,0 +1,514 @@
|
||||
# إجراءات عملية - البدء الفوري بالإصلاحات
|
||||
|
||||
**تاريخ التحديث:** 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** |
|
||||
Reference in New Issue
Block a user