feat: harden backend security with HMAC verification, SSL validation, and documentation updates while removing legacy scripts.

This commit is contained in:
Hamza-Ayed
2026-07-08 22:10:01 +03:00
parent 628e169552
commit 21877153eb
20 changed files with 6267 additions and 17 deletions
+1 -1
View File
@@ -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);
+2 -1
View File
@@ -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);
-3
View File
@@ -1,3 +0,0 @@
<?php
header('Content-Type: text/plain');
echo file_get_contents(__DIR__ . '/auth/loginFromGooglePassenger.php');
+18 -1
View File
@@ -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);
-5
View File
@@ -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;
}
+77
View File
@@ -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;
}
}
+11 -4
View File
@@ -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'
+2 -2
View File
@@ -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);