From d6f29802e016d95dedef19990b4f93106f66bcbb Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Wed, 17 Jun 2026 06:22:41 +0300 Subject: [PATCH] fix(security): fix pervasive IDOR - force JWT user identity in 9 endpoints, fix host injection, exception leaks, wallet auth --- backend/ride/cancelRide/add.php | 13 +++++++++--- backend/ride/invitor/add.php | 13 +++++++++--- backend/ride/invitor/claim.php | 19 +++++++++++++++-- backend/ride/rate/add.php | 7 ++++++- backend/ride/rate/addRateToDriver.php | 24 ++++++++-------------- backend/ride/rides/acceptRide.php | 3 ++- backend/ride/rides/add_ride.php | 4 +++- backend/ride/rides/finish_ride_updates.php | 3 ++- backend/uploadImagePortrate.php | 14 +++++++------ 9 files changed, 67 insertions(+), 33 deletions(-) diff --git a/backend/ride/cancelRide/add.php b/backend/ride/cancelRide/add.php index 57e3e12..93ba0e3 100644 --- a/backend/ride/cancelRide/add.php +++ b/backend/ride/cancelRide/add.php @@ -1,12 +1,19 @@ rowCount() > 0) { "expirationTime" => $expirationTime ]); } catch (PDOException $e) { - jsonError("Database error: " . $e->getMessage()); + error_log("[invitor/add] DB Error: " . $e->getMessage()); + jsonError("Database error occurred"); } } @@ -83,7 +89,8 @@ if ($checkStmt->rowCount() > 0) { jsonError("Failed to save invite data"); } } catch (PDOException $e) { - jsonError("Database error: " . $e->getMessage()); + error_log("[invitor/add] DB Error: " . $e->getMessage()); + jsonError("Database error occurred"); } } ?> \ No newline at end of file diff --git a/backend/ride/invitor/claim.php b/backend/ride/invitor/claim.php index bf5978e..cce608a 100644 --- a/backend/ride/invitor/claim.php +++ b/backend/ride/invitor/claim.php @@ -7,8 +7,17 @@ header('Content-Type: application/json'); try { $inviteId = filterRequest("invite_id"); - $driverId = filterRequest("driver_id"); - $passengerId = filterRequest("passenger_id"); + // Force user ID from JWT based on role + if ($role === 'driver') { + $driverId = $user_id; + $passengerId = null; + } elseif ($role === 'passenger') { + $passengerId = $user_id; + $driverId = null; + } else { + echo json_encode(["status" => "failure", "message" => "Invalid user role."]); + exit; + } $countryCode = filterRequest("country_code"); // Expected: Jordan, Syria, Egypt if (empty($inviteId)) { @@ -88,6 +97,7 @@ try { } function addWalletBalance($url, $userId, $userType, $amount) { + $s2sKey = getenv('S2S_SHARED_KEY'); $data = [ "user_id" => $userId, "user_type" => $userType, @@ -100,6 +110,11 @@ function addWalletBalance($url, $userId, $userType, $amount) { curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + if ($s2sKey) { + curl_setopt($ch, CURLOPT_HTTPHEADER, ["X-Auth-Token: $s2sKey"]); + } $response = curl_exec($ch); curl_close($ch); return $response; diff --git a/backend/ride/rate/add.php b/backend/ride/rate/add.php index ac7b0f5..2361c99 100644 --- a/backend/ride/rate/add.php +++ b/backend/ride/rate/add.php @@ -1,7 +1,12 @@ getMessage() . " | RideID: $ride_id \n"; - file_put_contents("errors.log", $errorMsg, FILE_APPEND); - - // 2. إرجاع رسالة خطأ عامة للتطبيق + error_log("[addRateToDriver] DB Error: " . $e->getMessage() . " | RideID: $ride_id"); jsonError("Database Error: Could not save rating"); } catch (Exception $e) { - // --- هذا القسم خاص بالأخطاء العامة الأخرى --- - - $errorMsg = "[" . date("Y-m-d H:i:s") . "] General Error: " . $e->getMessage() . "\n"; - file_put_contents("errors.log", $errorMsg, FILE_APPEND); - - jsonError("Error: " . $e->getMessage()); + error_log("[addRateToDriver] General Error: " . $e->getMessage()); + jsonError("Error: Could not save rating"); } ?> \ No newline at end of file diff --git a/backend/ride/rides/acceptRide.php b/backend/ride/rides/acceptRide.php index 487e2bc..7cd9c3f 100644 --- a/backend/ride/rides/acceptRide.php +++ b/backend/ride/rides/acceptRide.php @@ -17,7 +17,8 @@ try { // ── 1. Input & Validation ────────────────────────────────────── $rideId = filterRequest("id"); -$driverId = filterRequest("driver_id"); +// Force driver_id from JWT — never trust user-supplied driver_id +$driverId = $user_id; $status = filterRequest("status"); // القيمة التي يرسلها التطبيق: 'accepted' $passengerToken = filterRequest("passengerToken"); diff --git a/backend/ride/rides/add_ride.php b/backend/ride/rides/add_ride.php index 2246129..2db87cc 100644 --- a/backend/ride/rides/add_ride.php +++ b/backend/ride/rides/add_ride.php @@ -57,7 +57,9 @@ $start_location = filterRequest("start_location"); $end_location = filterRequest("end_location"); $price = filterRequest("price"); $price_token = filterRequest("price_token"); -$passenger_id = filterRequest("passenger_id"); + +// Force passenger_id from JWT — never trust user-supplied passenger_id +$passenger_id = $user_id; $driver_id = filterRequest("driver_id") ?: 0; $status = filterRequest("status"); $price_for_driver = filterRequest("price_for_driver"); diff --git a/backend/ride/rides/finish_ride_updates.php b/backend/ride/rides/finish_ride_updates.php index 0b7258c..4b3bc30 100644 --- a/backend/ride/rides/finish_ride_updates.php +++ b/backend/ride/rides/finish_ride_updates.php @@ -34,7 +34,8 @@ define('WALLET_PAYMENT_URL', 'https://walletintaleq.intaleq.xyz/v1/main/ride/pay // 1. Receive Raw Parameters (NO price from client) // ============================================================ $rideId = filterRequest("rideId"); -$driver_id = filterRequest("driver_id"); +// Force driver_id from JWT — never trust user-supplied driver_id +$driver_id = $user_id; $passengerId = filterRequest("passengerId"); $newStatus = filterRequest("status"); // Expected: "Finished" $actualDistance = filterRequest("actualDistance"); diff --git a/backend/uploadImagePortrate.php b/backend/uploadImagePortrate.php index 1961893..72303f5 100644 --- a/backend/uploadImagePortrate.php +++ b/backend/uploadImagePortrate.php @@ -25,12 +25,13 @@ try { $limiter = new RateLimiter($redis); $limiter->enforce(RateLimiter::identifier($user_id ?? null), 'upload'); - $driverID = filterRequest("driverID"); - uploadLog("📥 Received driverID: $driverID"); + // Force driverID from JWT — never trust user-supplied driverID + $driverID = $user_id; + uploadLog("📥 Using JWT driverID: $driverID"); if (empty($driverID)) { - uploadLog("❌ Driver ID is missing.", 'ERROR'); - jsonError('Driver ID is required.', 400); + uploadLog("❌ Driver ID from JWT is missing.", 'ERROR'); + jsonError('Authentication required.', 400); } // 2. استخدام دالة الرفع الآمنة (MIME check, random name, 5MB limit) @@ -47,8 +48,9 @@ try { uploadLog("✅ File moved successfully to: " . $uploadResult['path']); // 3. تحديث قاعدة البيانات ديناميكياً - $host = $_SERVER['HTTP_HOST'] ?? 'api.siromove.com'; - $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https" : "http"; + // Use configured domain instead of Host header to prevent host header injection + $host = getenv('APP_DOMAIN') ?: 'api.siromove.com'; + $protocol = 'https'; $linkImage = "$protocol://$host/siro/portrate_captain_image/" . $new_filename; // تأكد من أن الاتصال قادم من connect.php أو اجلبه