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)
This commit is contained in:
Hamza-Ayed
2026-06-17 07:56:57 +03:00
parent 50a5308f43
commit 3543fdd2cd
11 changed files with 64 additions and 81 deletions

View File

@@ -5,13 +5,16 @@ header('Content-Type: application/json');
uploadLog("🚀 [EgyptDocuments/uploadEgyptIdBack.php] Egyptian ID back upload started."); uploadLog("🚀 [EgyptDocuments/uploadEgyptIdBack.php] Egyptian ID back upload started.");
$driverID = filterRequest("driverID"); $rawDriverID = filterRequest("driverID");
if (empty($driverID)) { if (empty($rawDriverID)) {
uploadLog("❌ Missing driverID parameter.", 'ERROR'); uploadLog("❌ Missing driverID parameter.", 'ERROR');
jsonError("driverID is required."); jsonError("driverID is required.");
exit; exit;
} }
// منع path traversal
$driverID = basename($rawDriverID);
if (isset($_FILES['image'])) { if (isset($_FILES['image'])) {
uploadLog("$_FILES['image'] metadata", 'INFO', [ uploadLog("$_FILES['image'] metadata", 'INFO', [
'name' => $_FILES['image']['name'] ?? 'unknown', 'name' => $_FILES['image']['name'] ?? 'unknown',
@@ -33,19 +36,16 @@ if (!isset($_FILES['image']) || $_FILES['image']['error'] !== UPLOAD_ERR_OK) {
$image_file = $_FILES['image']; $image_file = $_FILES['image'];
$allowed_extensions = ['jpg', 'jpeg', 'png']; $allowed_extensions = ['jpg', 'jpeg', 'png'];
// Get file information
$image_name = $image_file['name']; $image_name = $image_file['name'];
$image_size = $image_file['size']; $image_size = $image_file['size'];
$image_extension = strtolower(pathinfo($image_name, PATHINFO_EXTENSION)); $image_extension = strtolower(pathinfo($image_name, PATHINFO_EXTENSION));
// Validate file extension
if (!in_array($image_extension, $allowed_extensions, true)) { if (!in_array($image_extension, $allowed_extensions, true)) {
uploadLog("❌ Invalid image format extension: .$image_extension", 'ERROR'); uploadLog("❌ Invalid image format extension: .$image_extension", 'ERROR');
jsonError("Invalid image format"); jsonError("Invalid image format");
exit; exit;
} }
// Validate MIME type
$finfo = finfo_open(FILEINFO_MIME_TYPE); $finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $image_file['tmp_name']); $mime_type = finfo_file($finfo, $image_file['tmp_name']);
finfo_close($finfo); finfo_close($finfo);
@@ -57,29 +57,25 @@ if (!in_array($mime_type, $allowed_mime_types, true)) {
exit; exit;
} }
// Generate a unique filename using driverID
$new_filename = $driverID . '.' . $image_extension; $new_filename = $driverID . '.' . $image_extension;
// Set target directory for uploads
$target_dir = __DIR__ . "/card_image/"; $target_dir = __DIR__ . "/card_image/";
if (!is_dir($target_dir)) { if (!is_dir($target_dir)) {
mkdir($target_dir, 0755, true); mkdir($target_dir, 0755, true);
} }
// Construct target file path
$target_file = $target_dir . $new_filename; $target_file = $target_dir . $new_filename;
// Move the image file to the target location
if (!move_uploaded_file($image_file['tmp_name'], $target_file)) { if (!move_uploaded_file($image_file['tmp_name'], $target_file)) {
uploadLog("❌ Failed to save image to target file: $target_file", 'ERROR'); uploadLog("❌ Failed to save image to target file: $target_file", 'ERROR');
jsonError("Failed to save image"); jsonError("Failed to save image");
exit; exit;
} }
// Resolve dynamic URL // استخدام النطاق من البيئة بدلاً من Host header
$host = $_SERVER['HTTP_HOST'] ?? 'api.siromove.com'; $domain = getenv('APP_DOMAIN') ?: 'api.siromove.com';
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https" : "http"; $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"); uploadLog("✅ Egypt ID back uploaded successfully. URL: $image_url");
@@ -89,4 +85,3 @@ printSuccess([
"file_link" => $image_url, "file_link" => $image_url,
"image_url" => $image_url "image_url" => $image_url
]); ]);
?>

View File

@@ -5,13 +5,16 @@ header('Content-Type: application/json');
uploadLog("🚀 [EgyptDocuments/uploadEgyptidFront.php] Egyptian ID front upload started."); uploadLog("🚀 [EgyptDocuments/uploadEgyptidFront.php] Egyptian ID front upload started.");
$driverID = filterRequest("driverID"); $rawDriverID = filterRequest("driverID");
if (empty($driverID)) { if (empty($rawDriverID)) {
uploadLog("❌ Missing driverID parameter.", 'ERROR'); uploadLog("❌ Missing driverID parameter.", 'ERROR');
jsonError("driverID is required."); jsonError("driverID is required.");
exit; exit;
} }
// منع path traversal
$driverID = basename($rawDriverID);
if (isset($_FILES['image'])) { if (isset($_FILES['image'])) {
uploadLog("$_FILES['image'] metadata", 'INFO', [ uploadLog("$_FILES['image'] metadata", 'INFO', [
'name' => $_FILES['image']['name'] ?? 'unknown', 'name' => $_FILES['image']['name'] ?? 'unknown',
@@ -33,19 +36,16 @@ if (!isset($_FILES['image']) || $_FILES['image']['error'] !== UPLOAD_ERR_OK) {
$image_file = $_FILES['image']; $image_file = $_FILES['image'];
$allowed_extensions = ['jpg', 'jpeg', 'png']; $allowed_extensions = ['jpg', 'jpeg', 'png'];
// Get file information
$image_name = $image_file['name']; $image_name = $image_file['name'];
$image_size = $image_file['size']; $image_size = $image_file['size'];
$image_extension = strtolower(pathinfo($image_name, PATHINFO_EXTENSION)); $image_extension = strtolower(pathinfo($image_name, PATHINFO_EXTENSION));
// Validate file extension
if (!in_array($image_extension, $allowed_extensions, true)) { if (!in_array($image_extension, $allowed_extensions, true)) {
uploadLog("❌ Invalid image format extension: .$image_extension", 'ERROR'); uploadLog("❌ Invalid image format extension: .$image_extension", 'ERROR');
jsonError("Invalid image format"); jsonError("Invalid image format");
exit; exit;
} }
// Validate MIME type
$finfo = finfo_open(FILEINFO_MIME_TYPE); $finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $image_file['tmp_name']); $mime_type = finfo_file($finfo, $image_file['tmp_name']);
finfo_close($finfo); finfo_close($finfo);
@@ -57,29 +57,25 @@ if (!in_array($mime_type, $allowed_mime_types, true)) {
exit; exit;
} }
// Generate a unique filename using driverID
$new_filename = $driverID . '.' . $image_extension; $new_filename = $driverID . '.' . $image_extension;
// Set target directory for uploads
$target_dir = __DIR__ . "/egypt/idFront/"; $target_dir = __DIR__ . "/egypt/idFront/";
if (!is_dir($target_dir)) { if (!is_dir($target_dir)) {
mkdir($target_dir, 0755, true); mkdir($target_dir, 0755, true);
} }
// Construct target file path
$target_file = $target_dir . $new_filename; $target_file = $target_dir . $new_filename;
// Move the image file to the target location
if (!move_uploaded_file($image_file['tmp_name'], $target_file)) { if (!move_uploaded_file($image_file['tmp_name'], $target_file)) {
uploadLog("❌ Failed to save image to target file: $target_file", 'ERROR'); uploadLog("❌ Failed to save image to target file: $target_file", 'ERROR');
jsonError("Failed to save image"); jsonError("Failed to save image");
exit; exit;
} }
// Resolve dynamic URL // استخدام النطاق من البيئة بدلاً من Host header
$host = $_SERVER['HTTP_HOST'] ?? 'api.siromove.com'; $domain = getenv('APP_DOMAIN') ?: 'api.siromove.com';
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https" : "http"; $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"); uploadLog("✅ Egypt ID front uploaded successfully. URL: $image_url");
@@ -89,4 +85,3 @@ printSuccess([
"file_link" => $image_url, "file_link" => $image_url,
"image_url" => $image_url "image_url" => $image_url
]); ]);
?>

View File

@@ -227,7 +227,7 @@ function getInternalSocketKey(): string
if ($key) { if ($key) {
return trim($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)) { if (file_exists($path)) {
return trim((string)@file_get_contents($path)); return trim((string)@file_get_contents($path));
} }

View File

@@ -268,7 +268,7 @@ function notifyPassengerOnRideServer($passenger_id, $payload) {
$INTERNAL_KEY = function_exists('getInternalSocketKey') ? getInternalSocketKey() : ''; $INTERNAL_KEY = function_exists('getInternalSocketKey') ? getInternalSocketKey() : '';
if (empty($INTERNAL_KEY)) { 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 = [ $postData = [

View File

@@ -1,47 +1,24 @@
<?php <?php
include 'connect.php'; require_once __DIR__ . '/core/bootstrap.php';
// نضمن أن الرد دائماً JSON
header('Content-Type: application/json; charset=utf-8'); header('Content-Type: application/json; charset=utf-8');
// 1) قراءة الـ body كـ JSON (من Flutter) // التحقق من صلاحية الأدمن عبر JWT
$jwtService = new JwtService($redis ?? null);
$admin = $jwtService->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'); $raw = file_get_contents('php://input');
$data = json_decode($raw, true); $data = json_decode($raw, true);
if (!is_array($data)) { if (!is_array($data)) {
// fallback لو أرسلت form-data أو x-www-form-urlencoded
$data = $_POST; $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'] ?? ''; $action = $data['action'] ?? '';
$text = trim($data['text'] ?? ''); $text = trim($data['text'] ?? '');
@@ -54,13 +31,10 @@ if ($text === '' || ($action !== 'encrypt' && $action !== 'decrypt')) {
exit; exit;
} }
// 4) تنفيذ التشفير / الفك
try { try {
// require_once __DIR__ . '/encrypt_decrypt.php';
if ($action === 'encrypt') { if ($action === 'encrypt') {
$result = $encryptionHelper->encryptData($text); $result = $encryptionHelper->encryptData($text);
} else { // decrypt } else {
$result = $encryptionHelper->decryptData($text); $result = $encryptionHelper->decryptData($text);
} }
@@ -70,6 +44,7 @@ try {
'result' => (string) $result, 'result' => (string) $result,
]); ]);
} catch (Exception $e) { } catch (Exception $e) {
error_log("[ggg.php] " . $e->getMessage());
http_response_code(500); http_response_code(500);
echo json_encode([ echo json_encode([
'status' => 'error', 'status' => 'error',

View File

@@ -11,7 +11,7 @@ ini_set('memory_limit', '512M');
require_once realpath(__DIR__ . '/../vendor/autoload.php'); require_once realpath(__DIR__ . '/../vendor/autoload.php');
require_once 'load_env.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); loadEnvironment($env_file);
include "encrypt_decrypt.php"; // لاستخدام $encryptionHelper include "encrypt_decrypt.php"; // لاستخدام $encryptionHelper

View File

@@ -3,7 +3,8 @@
header('Content-Type: application/json; charset=utf-8'); header('Content-Type: application/json; charset=utf-8');
require_once __DIR__ . '/../../load_env.php'; 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'; $servername = getenv('DB_REVERSE_GEO_HOST') ?: 'localhost';

View File

@@ -83,8 +83,8 @@ try {
// أ) Socket (إشعار السائق في التطبيق فوراً) // أ) Socket (إشعار السائق في التطبيق فوراً)
$socketUrl = 'http://188.68.36.205:2021'; $socketUrl = 'http://188.68.36.205:2021';
$internalKeyPath = '/home/siro-api/.internal_socket_key'; $internalKeyPath = getenv('INTERNAL_SOCKET_KEY_PATH') ?: '';
$internalKey = file_exists($internalKeyPath) ? trim(file_get_contents($internalKeyPath)) : ''; $internalKey = ($internalKeyPath && file_exists($internalKeyPath)) ? trim(file_get_contents($internalKeyPath)) : (getenv('INTERNAL_SOCKET_KEY') ?: '');
$ch = curl_init($socketUrl); $ch = curl_init($socketUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

View File

@@ -2,7 +2,13 @@
// test_socket_dispatch.php // test_socket_dispatch.php
$socketUrl = "http://188.68.36.205:2021"; $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 موجود عندك // جرّب Driver ID موجود عندك
$driverId = 691; $driverId = 691;

View File

@@ -8,6 +8,13 @@ if (!$driverID) {
exit; 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 DRIVER TABLE
--------------------------------------------------------- */ --------------------------------------------------------- */
@@ -20,13 +27,16 @@ $driverFieldsAllowed = [
"expirationDate", "created_at", "updated_at" "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 = [ $encryptedDriverFields = [
"phone", "email", "password", "national_number","gender", "name_arabic", "first_name", "phone", "email", "password", "national_number","gender", "name_arabic", "first_name",
"last_name", "birthdate", "site", "maritalStatus", "employmentType", "accountBank", "bankCode" "last_name", "birthdate", "site", "maritalStatus", "employmentType", "accountBank", "bankCode"
]; ];
$driverSet = []; $driverSet = [];
$driverParams = [":id" => $driverID]; $driverParams = [":id" => $driverID];
@@ -43,7 +53,6 @@ foreach ($driverFieldsAllowed as $field) {
} }
} }
// Execute Driver Update
$driverUpdated = false; $driverUpdated = false;
if (!empty($driverSet)) { if (!empty($driverSet)) {
$driverSql = "UPDATE `driver` SET " . implode(", ", $driverSet) . " WHERE `id` = :id"; $driverSql = "UPDATE `driver` SET " . implode(", ", $driverSet) . " WHERE `id` = :id";
@@ -65,7 +74,7 @@ $carSet = [];
$carParams = [":driverID" => $driverID]; $carParams = [":driverID" => $driverID];
foreach ($carFieldsAllowed as $field) { foreach ($carFieldsAllowed as $field) {
if ($field === "id") continue; // skip primary key in SET if ($field === "id") continue;
if (isset($_POST[$field]) && $_POST[$field] !== "") { if (isset($_POST[$field]) && $_POST[$field] !== "") {
$value = filterRequest($field); $value = filterRequest($field);
$carSet[] = "`$field` = :$field"; $carSet[] = "`$field` = :$field";
@@ -73,7 +82,6 @@ foreach ($carFieldsAllowed as $field) {
} }
} }
// Execute Car Update
$carUpdated = false; $carUpdated = false;
if (!empty($carSet)) { if (!empty($carSet)) {
$carSql = "UPDATE `CarRegistration` SET " . implode(", ", $carSet) . " WHERE `driverID` = :driverID"; $carSql = "UPDATE `CarRegistration` SET " . implode(", ", $carSet) . " WHERE `driverID` = :driverID";
@@ -82,12 +90,8 @@ if (!empty($carSet)) {
$carUpdated = $stmtCar->rowCount() > 0; $carUpdated = $stmtCar->rowCount() > 0;
} }
/* ---------------------------------------------------------
RESPONSE
--------------------------------------------------------- */
if ($driverUpdated || $carUpdated) { if ($driverUpdated || $carUpdated) {
jsonSuccess(null, "Driver & Car updated successfully"); jsonSuccess(null, "Driver & Car updated successfully");
} else { } else {
jsonError("No changes were applied"); jsonError("No changes were applied");
} }
?>

View File

@@ -3,7 +3,14 @@ header('Content-Type: application/json');
// !! تأكد أن هذا المفتاح يطابق المفتاح في تطبيق الأندرويد !! // !! تأكد أن هذا المفتاح يطابق المفتاح في تطبيق الأندرويد !!
//define('SECRET_KEY', 'YOUR_SUPER_SECRET_KEY_123__'); //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. التحقق من صحة الطلب --- // --- 1. التحقق من صحة الطلب ---
$authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? ''; $authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? '';