feat: harden backend security with HMAC verification, SSL validation, and documentation updates while removing legacy scripts.
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
// ── سجل تتبع ────────────────────────────────────────────
|
||||
$debugFile = __DIR__ . '/../../../logs/audit_debug.txt';
|
||||
$logDir = dirname($debugFile);
|
||||
if (!is_dir($logDir)) @mkdir($logDir, 0777, true);
|
||||
if (!is_dir($logDir)) @mkdir($logDir, 0750, true);
|
||||
|
||||
@file_put_contents($debugFile, "[" . date('Y-m-d H:i:s') . "] === REQUEST START ===\n", FILE_APPEND);
|
||||
|
||||
|
||||
@@ -85,7 +85,8 @@ function sendSilentFcmNotification($token, $data) {
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields));
|
||||
|
||||
$result = curl_exec($ch);
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
header('Content-Type: text/plain');
|
||||
echo file_get_contents(__DIR__ . '/auth/loginFromGooglePassenger.php');
|
||||
@@ -247,9 +247,26 @@ class JwtService
|
||||
$nonce = $_SERVER['HTTP_X_NONCE'] ?? '';
|
||||
$body = file_get_contents('php://input') ?: '';
|
||||
|
||||
// Replay protection: مُفعّلة فقط عند العملاء الذين يرسلون
|
||||
// Timestamp + Nonce فعلياً (بعض تدفقات الـ wallet القديمة لا ترسلهما بعد)
|
||||
if ($timestamp !== '' && $nonce !== '') {
|
||||
if (abs(time() - (int)$timestamp) > 300) {
|
||||
error_log("[SECURITY] HMAC timestamp expired | User: $userId | TS: '$timestamp'");
|
||||
self::abort(403, 'Request expired');
|
||||
}
|
||||
if ($this->redis) {
|
||||
$nonceKey = "hmac_nonce:{$userId}:{$nonce}";
|
||||
if ($this->redis->exists($nonceKey)) {
|
||||
error_log("[SECURITY] HMAC nonce replay detected | User: $userId | Nonce: $nonce");
|
||||
self::abort(403, 'Replay detected');
|
||||
}
|
||||
$this->redis->setex($nonceKey, 300, '1');
|
||||
}
|
||||
}
|
||||
|
||||
// اشتقاق مفتاح الـ HMAC الخاص بهذا المستخدم
|
||||
$userSecret = hash_hmac('sha256', (string)$userId, $this->hmacSecret);
|
||||
|
||||
|
||||
// المعادلة الموحدة: Body + Timestamp + Nonce
|
||||
$payloadToSign = $body . $timestamp . $nonce;
|
||||
$expectedHmac = hash_hmac('sha256', $payloadToSign, $userSecret);
|
||||
|
||||
@@ -45,11 +45,6 @@ class RateLimiter
|
||||
$this->redis->expire($key, $window);
|
||||
}
|
||||
|
||||
// Disable limit for register during debugging
|
||||
if ($type === 'register') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $current <= $max;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
{{ssl_certificate_key}}
|
||||
{{ssl_certificate}}
|
||||
server_name intaleqapp.com www.intaleqapp.com www1.intaleqapp.com;
|
||||
{{root}}
|
||||
|
||||
{{nginx_access_log}}
|
||||
{{nginx_error_log}}
|
||||
|
||||
# التحويل لـ HTTPS
|
||||
if ($scheme != "https") {
|
||||
rewrite ^ https://$host$uri permanent;
|
||||
}
|
||||
|
||||
{{settings}}
|
||||
|
||||
index index.php index.html index.htm;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$args;
|
||||
}
|
||||
|
||||
# إعدادات الروابط الخاصة بالتطبيقات (يجب أن تسبق قواعد حجب الملفات المخفية)
|
||||
location ^~ /.well-known/apple-app-site-association {
|
||||
default_type application/json;
|
||||
auth_basic off;
|
||||
allow all;
|
||||
}
|
||||
|
||||
location ~ /.well-known {
|
||||
auth_basic off;
|
||||
allow all;
|
||||
}
|
||||
|
||||
# منع الوصول الكامل لمجلد اللوجات (سجلات PHP، السجلات المخصصة)
|
||||
location ^~ /logs/ {
|
||||
deny all;
|
||||
}
|
||||
|
||||
# منع الوصول لملفات إدارة الحزم ومخرجات المشروع الداخلية
|
||||
location ~* ^/(composer\.(json|lock)|package(-lock)?\.json)$ {
|
||||
deny all;
|
||||
}
|
||||
|
||||
# منع الوصول لأي ملف/مجلد مخفي: .env, .git, .enckey, .secret_key, .htaccess, .DS_Store ...
|
||||
# (تأتي بعد قواعد .well-known أعلاه حتى لا تحجبها Nginx يفحص location~ بترتيب ظهورها بالملف)
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
|
||||
# معالجة ملفات PHP مباشرة دون وسيط
|
||||
location ~ \.php$ {
|
||||
include fastcgi_params;
|
||||
fastcgi_intercept_errors on;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
try_files $uri =404;
|
||||
fastcgi_read_timeout 3600;
|
||||
fastcgi_send_timeout 3600;
|
||||
fastcgi_pass 127.0.0.1:{{php_fpm_port}};
|
||||
fastcgi_param PHP_VALUE "{{php_settings}}";
|
||||
}
|
||||
|
||||
# معالجة الملفات الثابتة
|
||||
location ~* ^.+\.(css|js|jpg|jpeg|gif|png|ico|gz|svg|svgz|ttf|otf|woff|woff2|eot|mp4|ogg|ogv|webm|webp|zip|swf|map|mjs)$ {
|
||||
add_header Access-Control-Allow-Origin "*";
|
||||
expires max;
|
||||
access_log off;
|
||||
try_files $uri =404;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,13 @@ if (empty($user_id) || $role !== 'driver') {
|
||||
exit;
|
||||
}
|
||||
|
||||
// 1.1 HMAC إلزامي على تحويلات المحفظة (عملية حساسة تحرّك أموالاً حقيقية)
|
||||
if (empty($_SERVER['HTTP_X_HMAC_AUTH'] ?? null)) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['status' => 'error', 'message' => 'Request verification required']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$senderID = $user_id; // ✅ من JWT
|
||||
$receiverPhone = filterRequest('receiverPhone');
|
||||
$amount = filterRequest('amount');
|
||||
@@ -74,7 +81,7 @@ $paymentServerUrl = "$walletServer/v2/main/ride/driverWallet/transfer.php";
|
||||
$postData = [
|
||||
'senderID' => $senderID,
|
||||
'receiverID' => $receiverID,
|
||||
'amount' => $amount,
|
||||
'amount' => $amountInt,
|
||||
'country' => $country
|
||||
];
|
||||
|
||||
@@ -108,15 +115,15 @@ if ($httpCode === 200 && isset($paymentResponse['status']) && $paymentResponse['
|
||||
if (!empty($receiver['fcm_token'])) {
|
||||
$senderName = $decodedToken->name ?? 'A driver'; // Optional: Fetch sender name
|
||||
|
||||
$fcmBody = "You have received a transfer of " . $amount . " from " . $senderName;
|
||||
$fcmBody = "You have received a transfer of " . $amountInt . " from " . $senderName;
|
||||
// Arabic fallback if name available
|
||||
$fcmBodyAr = "لقد تلقيت حوالة بقيمة " . $amount . " من " . $senderName;
|
||||
$fcmBodyAr = "لقد تلقيت حوالة بقيمة " . $amountInt . " من " . $senderName;
|
||||
|
||||
sendFCM_Internal(
|
||||
$receiver['fcm_token'],
|
||||
"Transfer Received",
|
||||
$fcmBodyAr,
|
||||
['type' => 'transfer', 'amount' => $amount],
|
||||
['type' => 'transfer', 'amount' => $amountInt],
|
||||
'Transfer',
|
||||
false,
|
||||
'ding'
|
||||
|
||||
@@ -14,7 +14,7 @@ if (empty($secretKey)) {
|
||||
|
||||
// --- 1. التحقق من صحة الطلب ---
|
||||
$authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
|
||||
if ($authHeader !== 'Bearer ' . $secretKey) {
|
||||
if (empty($secretKey) || !hash_equals('Bearer ' . $secretKey, $authHeader)) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['status' => 'error', 'message' => 'Unauthorized Access']);
|
||||
exit();
|
||||
@@ -74,7 +74,7 @@ if (preg_match($pattern_orangemoney_jo, $message_body, $matches)) {
|
||||
|
||||
// كتابة كل شيء في ملف السجل (بالمسار المطلق)
|
||||
$logDir = __DIR__ . '/../../logs';
|
||||
if (!is_dir($logDir)) @mkdir($logDir, 0777, true);
|
||||
if (!is_dir($logDir)) @mkdir($logDir, 0750, true);
|
||||
file_put_contents($logDir . '/sms_webhook_log.txt', $log_entry, FILE_APPEND);
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user