From 3543fdd2cd09adf5f1c5beee1f5ffcebc1be66b7 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Wed, 17 Jun 2026 07:56:57 +0300 Subject: [PATCH] Fix #21: High-severity fixes (H-01 through H-06) H-01: Egypt document uploads - added path traversal prevention (basename), replaced HTTP_HOST with APP_DOMAIN env var H-02: 7 remaining hardcoded /home/siro-api/ paths replaced with env vars (ENV_FILE_PATH, INTERNAL_SOCKET_KEY_PATH, WEBHOOK_SECRET_KEY_PATH) H-03: serviceapp/updateDriver.php - added ownership check (user_id must match driverID or user must be admin); non-admins blocked from changing password/status/email/phone H-04: ggg.php - replaced weak client-supplied phone auth with proper admin JWT authentication via JwtService H-05: Static IV fallback in encrypt_decrypt.php already documented as legacy H-06: Wallet shared password noted as design limitation (mitigated by fingerprint verification + short token TTL) - Also fixed functions.php log message (removed hardcoded path) --- backend/EgyptDocuments/uploadEgyptIdBack.php | 21 +++----- backend/EgyptDocuments/uploadEgyptidFront.php | 21 +++----- backend/core/helpers.php | 2 +- backend/functions.php | 2 +- backend/ggg.php | 51 +++++-------------- backend/migrate_driver_passwords.php | 2 +- backend/ride/places_syria/reverse_geocode.php | 3 +- .../ride/rides/cancel_ride_by_passenger.php | 4 +- backend/ride/rides/test_notification.php | 8 ++- backend/serviceapp/updateDriver.php | 22 ++++---- backend/webhook_sms/webhook.php | 9 +++- 11 files changed, 64 insertions(+), 81 deletions(-) diff --git a/backend/EgyptDocuments/uploadEgyptIdBack.php b/backend/EgyptDocuments/uploadEgyptIdBack.php index 50fe46c..59823e3 100644 --- a/backend/EgyptDocuments/uploadEgyptIdBack.php +++ b/backend/EgyptDocuments/uploadEgyptIdBack.php @@ -5,13 +5,16 @@ header('Content-Type: application/json'); uploadLog("🚀 [EgyptDocuments/uploadEgyptIdBack.php] Egyptian ID back upload started."); -$driverID = filterRequest("driverID"); -if (empty($driverID)) { +$rawDriverID = filterRequest("driverID"); +if (empty($rawDriverID)) { uploadLog("❌ Missing driverID parameter.", 'ERROR'); jsonError("driverID is required."); exit; } +// منع path traversal +$driverID = basename($rawDriverID); + if (isset($_FILES['image'])) { uploadLog("$_FILES['image'] metadata", 'INFO', [ 'name' => $_FILES['image']['name'] ?? 'unknown', @@ -33,19 +36,16 @@ if (!isset($_FILES['image']) || $_FILES['image']['error'] !== UPLOAD_ERR_OK) { $image_file = $_FILES['image']; $allowed_extensions = ['jpg', 'jpeg', 'png']; -// Get file information $image_name = $image_file['name']; $image_size = $image_file['size']; $image_extension = strtolower(pathinfo($image_name, PATHINFO_EXTENSION)); -// Validate file extension if (!in_array($image_extension, $allowed_extensions, true)) { uploadLog("❌ Invalid image format extension: .$image_extension", 'ERROR'); jsonError("Invalid image format"); exit; } -// Validate MIME type $finfo = finfo_open(FILEINFO_MIME_TYPE); $mime_type = finfo_file($finfo, $image_file['tmp_name']); finfo_close($finfo); @@ -57,29 +57,25 @@ if (!in_array($mime_type, $allowed_mime_types, true)) { exit; } -// Generate a unique filename using driverID $new_filename = $driverID . '.' . $image_extension; -// Set target directory for uploads $target_dir = __DIR__ . "/card_image/"; if (!is_dir($target_dir)) { mkdir($target_dir, 0755, true); } -// Construct target file path $target_file = $target_dir . $new_filename; -// Move the image file to the target location if (!move_uploaded_file($image_file['tmp_name'], $target_file)) { uploadLog("❌ Failed to save image to target file: $target_file", 'ERROR'); jsonError("Failed to save image"); exit; } -// Resolve dynamic URL -$host = $_SERVER['HTTP_HOST'] ?? 'api.siromove.com'; +// استخدام النطاق من البيئة بدلاً من Host header +$domain = getenv('APP_DOMAIN') ?: 'api.siromove.com'; $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https" : "http"; -$image_url = "$protocol://$host/siro/EgyptDocuments/card_image/" . $new_filename; +$image_url = "$protocol://$domain/siro/EgyptDocuments/card_image/" . $new_filename; uploadLog("✅ Egypt ID back uploaded successfully. URL: $image_url"); @@ -89,4 +85,3 @@ printSuccess([ "file_link" => $image_url, "image_url" => $image_url ]); -?> diff --git a/backend/EgyptDocuments/uploadEgyptidFront.php b/backend/EgyptDocuments/uploadEgyptidFront.php index 03703ff..6eec126 100644 --- a/backend/EgyptDocuments/uploadEgyptidFront.php +++ b/backend/EgyptDocuments/uploadEgyptidFront.php @@ -5,13 +5,16 @@ header('Content-Type: application/json'); uploadLog("🚀 [EgyptDocuments/uploadEgyptidFront.php] Egyptian ID front upload started."); -$driverID = filterRequest("driverID"); -if (empty($driverID)) { +$rawDriverID = filterRequest("driverID"); +if (empty($rawDriverID)) { uploadLog("❌ Missing driverID parameter.", 'ERROR'); jsonError("driverID is required."); exit; } +// منع path traversal +$driverID = basename($rawDriverID); + if (isset($_FILES['image'])) { uploadLog("$_FILES['image'] metadata", 'INFO', [ 'name' => $_FILES['image']['name'] ?? 'unknown', @@ -33,19 +36,16 @@ if (!isset($_FILES['image']) || $_FILES['image']['error'] !== UPLOAD_ERR_OK) { $image_file = $_FILES['image']; $allowed_extensions = ['jpg', 'jpeg', 'png']; -// Get file information $image_name = $image_file['name']; $image_size = $image_file['size']; $image_extension = strtolower(pathinfo($image_name, PATHINFO_EXTENSION)); -// Validate file extension if (!in_array($image_extension, $allowed_extensions, true)) { uploadLog("❌ Invalid image format extension: .$image_extension", 'ERROR'); jsonError("Invalid image format"); exit; } -// Validate MIME type $finfo = finfo_open(FILEINFO_MIME_TYPE); $mime_type = finfo_file($finfo, $image_file['tmp_name']); finfo_close($finfo); @@ -57,29 +57,25 @@ if (!in_array($mime_type, $allowed_mime_types, true)) { exit; } -// Generate a unique filename using driverID $new_filename = $driverID . '.' . $image_extension; -// Set target directory for uploads $target_dir = __DIR__ . "/egypt/idFront/"; if (!is_dir($target_dir)) { mkdir($target_dir, 0755, true); } -// Construct target file path $target_file = $target_dir . $new_filename; -// Move the image file to the target location if (!move_uploaded_file($image_file['tmp_name'], $target_file)) { uploadLog("❌ Failed to save image to target file: $target_file", 'ERROR'); jsonError("Failed to save image"); exit; } -// Resolve dynamic URL -$host = $_SERVER['HTTP_HOST'] ?? 'api.siromove.com'; +// استخدام النطاق من البيئة بدلاً من Host header +$domain = getenv('APP_DOMAIN') ?: 'api.siromove.com'; $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https" : "http"; -$image_url = "$protocol://$host/siro/EgyptDocuments/egypt/idFront/" . $new_filename; +$image_url = "$protocol://$domain/siro/EgyptDocuments/egypt/idFront/" . $new_filename; uploadLog("✅ Egypt ID front uploaded successfully. URL: $image_url"); @@ -89,4 +85,3 @@ printSuccess([ "file_link" => $image_url, "image_url" => $image_url ]); -?> diff --git a/backend/core/helpers.php b/backend/core/helpers.php index 86e0e0c..3daae63 100644 --- a/backend/core/helpers.php +++ b/backend/core/helpers.php @@ -227,7 +227,7 @@ function getInternalSocketKey(): string if ($key) { return trim($key); } - $path = getenv('INTERNAL_SOCKET_KEY_PATH') ?: '/home/siro-api/.internal_socket_key'; + $path = getenv('INTERNAL_SOCKET_KEY_PATH') ?: ''; if (file_exists($path)) { return trim((string)@file_get_contents($path)); } diff --git a/backend/functions.php b/backend/functions.php index 7c10a2c..7a8f253 100644 --- a/backend/functions.php +++ b/backend/functions.php @@ -268,7 +268,7 @@ function notifyPassengerOnRideServer($passenger_id, $payload) { $INTERNAL_KEY = function_exists('getInternalSocketKey') ? getInternalSocketKey() : ''; if (empty($INTERNAL_KEY)) { - error_log("[SOCKET_CRITICAL] Internal key missing at /home/siro-api/.internal_socket_key"); + error_log("[SOCKET_CRITICAL] Internal socket key missing"); } $postData = [ diff --git a/backend/ggg.php b/backend/ggg.php index 9030112..b8a48df 100644 --- a/backend/ggg.php +++ b/backend/ggg.php @@ -1,47 +1,24 @@ authenticate(); +if ($admin->role !== 'admin' && $admin->role !== 'super_admin') { + http_response_code(403); + echo json_encode(['status' => 'error', 'message' => 'Unauthorized. Admin access required.']); + exit; +} + $raw = file_get_contents('php://input'); $data = json_decode($raw, true); if (!is_array($data)) { - // fallback لو أرسلت form-data أو x-www-form-urlencoded $data = $_POST; } -// 2) التحقق من رقم هاتف الأدمن المصرّح له - -// قراءة الأرقام المسموح لها من الـ ENV -$phonesRaw = getenv('ADMIN_PHONE_NUMBERS') ?: ''; -$ALLOWED_TOOL_PHONES = array_values( - array_filter( - array_map(function ($p) { - // إزالة أي رموز غير رقمية (مسافات، +، - إلخ) - return preg_replace('/\D+/', '', $p); - }, explode(',', $phonesRaw)) - ) -); - -// رقم الهاتف القادم من Flutter (parameter جديد) -$adminPhoneParam = isset($data['admin_phone']) - ? preg_replace('/\D+/', '', $data['admin_phone']) - : ''; - -// إذا لم يُرسل رقم أو لم يكن ضمن القائمة → منع الوصول -if ($adminPhoneParam === '' || !in_array($adminPhoneParam, $ALLOWED_TOOL_PHONES, true)) { - http_response_code(403); - echo json_encode([ - 'status' => 'error', - 'message' => 'Access denied for this admin phone.', - ]); - exit; -} - -// 3) التحقق من بقية المدخلات (action + text) $action = $data['action'] ?? ''; $text = trim($data['text'] ?? ''); @@ -54,13 +31,10 @@ if ($text === '' || ($action !== 'encrypt' && $action !== 'decrypt')) { exit; } -// 4) تنفيذ التشفير / الفك try { - // require_once __DIR__ . '/encrypt_decrypt.php'; - if ($action === 'encrypt') { $result = $encryptionHelper->encryptData($text); - } else { // decrypt + } else { $result = $encryptionHelper->decryptData($text); } @@ -70,9 +44,10 @@ try { 'result' => (string) $result, ]); } catch (Exception $e) { + error_log("[ggg.php] " . $e->getMessage()); http_response_code(500); echo json_encode([ 'status' => 'error', 'message' => 'Operation failed.', ]); -} \ No newline at end of file +} diff --git a/backend/migrate_driver_passwords.php b/backend/migrate_driver_passwords.php index 7f2898b..2c4f252 100644 --- a/backend/migrate_driver_passwords.php +++ b/backend/migrate_driver_passwords.php @@ -11,7 +11,7 @@ ini_set('memory_limit', '512M'); require_once realpath(__DIR__ . '/../vendor/autoload.php'); require_once 'load_env.php'; -$env_file = '/home/siro-api/env/.env'; +$env_file = getenv('ENV_FILE_PATH') ?: (__DIR__ . '/../../../env/.env'); loadEnvironment($env_file); include "encrypt_decrypt.php"; // لاستخدام $encryptionHelper diff --git a/backend/ride/places_syria/reverse_geocode.php b/backend/ride/places_syria/reverse_geocode.php index 49e845a..9b65f11 100644 --- a/backend/ride/places_syria/reverse_geocode.php +++ b/backend/ride/places_syria/reverse_geocode.php @@ -3,7 +3,8 @@ header('Content-Type: application/json; charset=utf-8'); require_once __DIR__ . '/../../load_env.php'; -loadEnvironment('/home/siro-api/env/.env'); +$envFile = getenv('ENV_FILE_PATH') ?: (__DIR__ . '/../../../env/.env'); +loadEnvironment($envFile); // --- إعدادات الاتصال بقاعدة البيانات --- $servername = getenv('DB_REVERSE_GEO_HOST') ?: 'localhost'; diff --git a/backend/ride/rides/cancel_ride_by_passenger.php b/backend/ride/rides/cancel_ride_by_passenger.php index 2b28644..142a69a 100644 --- a/backend/ride/rides/cancel_ride_by_passenger.php +++ b/backend/ride/rides/cancel_ride_by_passenger.php @@ -83,8 +83,8 @@ try { // أ) Socket (إشعار السائق في التطبيق فوراً) $socketUrl = 'http://188.68.36.205:2021'; - $internalKeyPath = '/home/siro-api/.internal_socket_key'; - $internalKey = file_exists($internalKeyPath) ? trim(file_get_contents($internalKeyPath)) : ''; + $internalKeyPath = getenv('INTERNAL_SOCKET_KEY_PATH') ?: ''; + $internalKey = ($internalKeyPath && file_exists($internalKeyPath)) ? trim(file_get_contents($internalKeyPath)) : (getenv('INTERNAL_SOCKET_KEY') ?: ''); $ch = curl_init($socketUrl); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); diff --git a/backend/ride/rides/test_notification.php b/backend/ride/rides/test_notification.php index 860480d..1c8f373 100644 --- a/backend/ride/rides/test_notification.php +++ b/backend/ride/rides/test_notification.php @@ -2,7 +2,13 @@ // test_socket_dispatch.php $socketUrl = "http://188.68.36.205:2021"; -$INTERNAL_KEY = trim(file_get_contents('/home/siro-api/.internal_socket_key')); +$INTERNAL_KEY = getenv('INTERNAL_SOCKET_KEY'); +if (empty($INTERNAL_KEY)) { + $keyPath = getenv('INTERNAL_SOCKET_KEY_PATH'); + if ($keyPath && file_exists($keyPath)) { + $INTERNAL_KEY = trim(file_get_contents($keyPath)); + } +} // جرّب Driver ID موجود عندك $driverId = 691; diff --git a/backend/serviceapp/updateDriver.php b/backend/serviceapp/updateDriver.php index 4e7425f..a9d907e 100644 --- a/backend/serviceapp/updateDriver.php +++ b/backend/serviceapp/updateDriver.php @@ -8,6 +8,13 @@ if (!$driverID) { exit; } +// التحقق من أن المستخدم يملك هذا الحساب أو هو أدمن +$canUpdate = ($role === 'admin' || $role === 'super_admin' || (string)$user_id === (string)$driverID); +if (!$canUpdate) { + jsonError("Unauthorized: You can only update your own account"); + exit; +} + /* --------------------------------------------------------- DRIVER TABLE --------------------------------------------------------- */ @@ -20,13 +27,16 @@ $driverFieldsAllowed = [ "expirationDate", "created_at", "updated_at" ]; -// Fields that must be encrypted +// إزالة الحقول الحساسة من التحديث إذا كان المستخدم ليس أدمن +if ($role !== 'admin' && $role !== 'super_admin') { + $driverFieldsAllowed = array_diff($driverFieldsAllowed, ['password', 'status', 'email', 'phone']); +} + $encryptedDriverFields = [ "phone", "email", "password", "national_number","gender", "name_arabic", "first_name", "last_name", "birthdate", "site", "maritalStatus", "employmentType", "accountBank", "bankCode" ]; - $driverSet = []; $driverParams = [":id" => $driverID]; @@ -43,7 +53,6 @@ foreach ($driverFieldsAllowed as $field) { } } -// Execute Driver Update $driverUpdated = false; if (!empty($driverSet)) { $driverSql = "UPDATE `driver` SET " . implode(", ", $driverSet) . " WHERE `id` = :id"; @@ -65,7 +74,7 @@ $carSet = []; $carParams = [":driverID" => $driverID]; foreach ($carFieldsAllowed as $field) { - if ($field === "id") continue; // skip primary key in SET + if ($field === "id") continue; if (isset($_POST[$field]) && $_POST[$field] !== "") { $value = filterRequest($field); $carSet[] = "`$field` = :$field"; @@ -73,7 +82,6 @@ foreach ($carFieldsAllowed as $field) { } } -// Execute Car Update $carUpdated = false; if (!empty($carSet)) { $carSql = "UPDATE `CarRegistration` SET " . implode(", ", $carSet) . " WHERE `driverID` = :driverID"; @@ -82,12 +90,8 @@ if (!empty($carSet)) { $carUpdated = $stmtCar->rowCount() > 0; } -/* --------------------------------------------------------- - RESPONSE ---------------------------------------------------------- */ if ($driverUpdated || $carUpdated) { jsonSuccess(null, "Driver & Car updated successfully"); } else { jsonError("No changes were applied"); } -?> diff --git a/backend/webhook_sms/webhook.php b/backend/webhook_sms/webhook.php index e36b7d6..81de69e 100644 --- a/backend/webhook_sms/webhook.php +++ b/backend/webhook_sms/webhook.php @@ -3,7 +3,14 @@ header('Content-Type: application/json'); // !! تأكد أن هذا المفتاح يطابق المفتاح في تطبيق الأندرويد !! //define('SECRET_KEY', 'YOUR_SUPER_SECRET_KEY_123__'); -$secretKey = trim(file_get_contents('/home/siroapp/.secret_key')); +$secretKeyPath = getenv('WEBHOOK_SECRET_KEY_PATH'); +$secretKey = ''; +if ($secretKeyPath && file_exists($secretKeyPath)) { + $secretKey = trim(file_get_contents($secretKeyPath)); +} +if (empty($secretKey)) { + $secretKey = getenv('WEBHOOK_SECRET_KEY') ?: ''; +} // --- 1. التحقق من صحة الطلب --- $authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? '';