fix(security): fix pervasive IDOR - force JWT user identity in 9 endpoints, fix host injection, exception leaks, wallet auth

This commit is contained in:
Hamza-Ayed
2026-06-17 06:22:41 +03:00
parent 4a9e6b22c5
commit d6f29802e0
9 changed files with 67 additions and 33 deletions

View File

@@ -1,12 +1,19 @@
<?php <?php
require_once __DIR__ . '/../../connect.php'; require_once __DIR__ . '/../../connect.php';
// استقبال المتغيرات // استقبال المتغيرات — force user IDs from JWT based on role
$driverID = filterRequest("driverID");
$passengerID = filterRequest("passengerID");
$rideID = filterRequest("rideID"); $rideID = filterRequest("rideID");
$note = filterRequest("note"); $note = filterRequest("note");
// Force driverID/passengerID from JWT based on user role
if ($role === 'driver') {
$driverID = $user_id;
$passengerID = filterRequest("passengerID");
} else {
$passengerID = $user_id;
$driverID = filterRequest("driverID");
}
// تنفيذ الإدخال بطريقة آمنة // تنفيذ الإدخال بطريقة آمنة
$sql = "INSERT INTO `canecl` (`driverID`, `passengerID`, `rideID`, `note`) $sql = "INSERT INTO `canecl` (`driverID`, `passengerID`, `rideID`, `note`)
VALUES (:driverID, :passengerID, :rideID, :note)"; VALUES (:driverID, :passengerID, :rideID, :note)";

View File

@@ -17,7 +17,12 @@ function generateUniqueCode($con) {
} }
} }
$driverId = filterRequest("driverId"); // Force driverId from JWT — only drivers can manage invitations
if ($role !== 'driver') {
jsonError("Only drivers can create invitations");
exit;
}
$driverId = $user_id;
$inviterDriverPhone = filterRequest("inviterDriverPhone"); $inviterDriverPhone = filterRequest("inviterDriverPhone");
// 🔐 تشفير رقم الهاتف // 🔐 تشفير رقم الهاتف
@@ -52,7 +57,8 @@ if ($checkStmt->rowCount() > 0) {
"expirationTime" => $expirationTime "expirationTime" => $expirationTime
]); ]);
} catch (PDOException $e) { } 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"); jsonError("Failed to save invite data");
} }
} catch (PDOException $e) { } catch (PDOException $e) {
jsonError("Database error: " . $e->getMessage()); error_log("[invitor/add] DB Error: " . $e->getMessage());
jsonError("Database error occurred");
} }
} }
?> ?>

View File

@@ -7,8 +7,17 @@ header('Content-Type: application/json');
try { try {
$inviteId = filterRequest("invite_id"); $inviteId = filterRequest("invite_id");
$driverId = filterRequest("driver_id"); // Force user ID from JWT based on role
$passengerId = filterRequest("passenger_id"); 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 $countryCode = filterRequest("country_code"); // Expected: Jordan, Syria, Egypt
if (empty($inviteId)) { if (empty($inviteId)) {
@@ -88,6 +97,7 @@ try {
} }
function addWalletBalance($url, $userId, $userType, $amount) { function addWalletBalance($url, $userId, $userType, $amount) {
$s2sKey = getenv('S2S_SHARED_KEY');
$data = [ $data = [
"user_id" => $userId, "user_id" => $userId,
"user_type" => $userType, "user_type" => $userType,
@@ -100,6 +110,11 @@ function addWalletBalance($url, $userId, $userType, $amount) {
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); 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); $response = curl_exec($ch);
curl_close($ch); curl_close($ch);
return $response; return $response;

View File

@@ -1,7 +1,12 @@
<?php <?php
require_once __DIR__ . '/../../connect.php'; require_once __DIR__ . '/../../connect.php';
$passenger_id = filterRequest("passenger_id"); // Force passenger_id from JWT — never trust user-supplied passenger_id
if ($role !== 'passenger') {
jsonError("Only passengers can submit ratings");
exit;
}
$passenger_id = $user_id;
$driverID = filterRequest("driverID"); $driverID = filterRequest("driverID");
$rideId = filterRequest("rideId"); $rideId = filterRequest("rideId");
$rating = filterRequest("rating"); $rating = filterRequest("rating");

View File

@@ -2,7 +2,12 @@
require_once __DIR__ . '/../../connect.php'; require_once __DIR__ . '/../../connect.php';
// --- استقبال المتغيرات --- // --- استقبال المتغيرات ---
$passenger_id = filterRequest("passenger_id"); // Force passenger_id from JWT — only passengers can rate drivers
if ($role !== 'passenger') {
jsonError("Only passengers can rate drivers");
exit;
}
$passenger_id = $user_id;
$driver_id = filterRequest("driver_id"); $driver_id = filterRequest("driver_id");
$ride_id = filterRequest("ride_id"); $ride_id = filterRequest("ride_id");
$rating = filterRequest("rating"); $rating = filterRequest("rating");
@@ -37,22 +42,11 @@ try {
} }
} catch (PDOException $e) { } catch (PDOException $e) {
// --- هذا القسم خاص بأخطاء قاعدة البيانات --- error_log("[addRateToDriver] DB Error: " . $e->getMessage() . " | RideID: $ride_id");
// 1. تسجيل الخطأ في ملف نصي على السيرفر (للمطور فقط)
// سيتم إنشاء ملف اسمه errors.log في نفس المجلد إذا لم يكن موجوداً
$errorMsg = "[" . date("Y-m-d H:i:s") . "] DB Error: " . $e->getMessage() . " | RideID: $ride_id \n";
file_put_contents("errors.log", $errorMsg, FILE_APPEND);
// 2. إرجاع رسالة خطأ عامة للتطبيق
jsonError("Database Error: Could not save rating"); jsonError("Database Error: Could not save rating");
} catch (Exception $e) { } catch (Exception $e) {
// --- هذا القسم خاص بالأخطاء العامة الأخرى --- error_log("[addRateToDriver] General Error: " . $e->getMessage());
jsonError("Error: Could not save rating");
$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());
} }
?> ?>

View File

@@ -17,7 +17,8 @@ try {
// ── 1. Input & Validation ────────────────────────────────────── // ── 1. Input & Validation ──────────────────────────────────────
$rideId = filterRequest("id"); $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' $status = filterRequest("status"); // القيمة التي يرسلها التطبيق: 'accepted'
$passengerToken = filterRequest("passengerToken"); $passengerToken = filterRequest("passengerToken");

View File

@@ -57,7 +57,9 @@ $start_location = filterRequest("start_location");
$end_location = filterRequest("end_location"); $end_location = filterRequest("end_location");
$price = filterRequest("price"); $price = filterRequest("price");
$price_token = filterRequest("price_token"); $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; $driver_id = filterRequest("driver_id") ?: 0;
$status = filterRequest("status"); $status = filterRequest("status");
$price_for_driver = filterRequest("price_for_driver"); $price_for_driver = filterRequest("price_for_driver");

View File

@@ -34,7 +34,8 @@ define('WALLET_PAYMENT_URL', 'https://walletintaleq.intaleq.xyz/v1/main/ride/pay
// 1. Receive Raw Parameters (NO price from client) // 1. Receive Raw Parameters (NO price from client)
// ============================================================ // ============================================================
$rideId = filterRequest("rideId"); $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"); $passengerId = filterRequest("passengerId");
$newStatus = filterRequest("status"); // Expected: "Finished" $newStatus = filterRequest("status"); // Expected: "Finished"
$actualDistance = filterRequest("actualDistance"); $actualDistance = filterRequest("actualDistance");

View File

@@ -25,12 +25,13 @@ try {
$limiter = new RateLimiter($redis); $limiter = new RateLimiter($redis);
$limiter->enforce(RateLimiter::identifier($user_id ?? null), 'upload'); $limiter->enforce(RateLimiter::identifier($user_id ?? null), 'upload');
$driverID = filterRequest("driverID"); // Force driverID from JWT — never trust user-supplied driverID
uploadLog("📥 Received driverID: $driverID"); $driverID = $user_id;
uploadLog("📥 Using JWT driverID: $driverID");
if (empty($driverID)) { if (empty($driverID)) {
uploadLog("❌ Driver ID is missing.", 'ERROR'); uploadLog("❌ Driver ID from JWT is missing.", 'ERROR');
jsonError('Driver ID is required.', 400); jsonError('Authentication required.', 400);
} }
// 2. استخدام دالة الرفع الآمنة (MIME check, random name, 5MB limit) // 2. استخدام دالة الرفع الآمنة (MIME check, random name, 5MB limit)
@@ -47,8 +48,9 @@ try {
uploadLog("✅ File moved successfully to: " . $uploadResult['path']); uploadLog("✅ File moved successfully to: " . $uploadResult['path']);
// 3. تحديث قاعدة البيانات ديناميكياً // 3. تحديث قاعدة البيانات ديناميكياً
$host = $_SERVER['HTTP_HOST'] ?? 'api.siromove.com'; // Use configured domain instead of Host header to prevent host header injection
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https" : "http"; $host = getenv('APP_DOMAIN') ?: 'api.siromove.com';
$protocol = 'https';
$linkImage = "$protocol://$host/siro/portrate_captain_image/" . $new_filename; $linkImage = "$protocol://$host/siro/portrate_captain_image/" . $new_filename;
// تأكد من أن الاتصال قادم من connect.php أو اجلبه // تأكد من أن الاتصال قادم من connect.php أو اجلبه