Files
Siro/docs/08_security/التقرير_الأمني_Backend_Siro.md

21 KiB

lang, dir
lang dir
ar rtl

🔐 تقرير الفحص الأمني الشامل — Backend تطبيق Siro

التاريخ: 8 يوليو 2026
الفاحص: مدقق أمني مستقل
النسخة: 1.0


📊 ملخص تنفيذي

تم فحص backend Siro فحصاً أمنياً شاملاً، شمل:

  • بنية المصادقة والتفويض (JWT، OTP، Rate Limiting)
  • طبقة قاعدة البيانات والاستعلامات
  • إدارة الأسرار والتشفير
  • نقاط الدخول الحساسة (تسجيل الدخول، رفع الملفات، تحويلات المحفظة)
  • أمان الويب العام (CORS، Headers، إدارة الأخطاء)

📈 التقييم العام

المستوى: جيد جداً ✅
الدرجة: 8.2/10

Backend مبني بمستوى أمني واعي وقوي. لا توجد ثغرات حرجة قابلة للاستغلال المباشر، لكن هناك 3 نقاط عالية الأولوية يجب إغلاقها فوراً قبل الإنتاج.


✅ نقاط القوة (ما تم إنجازه بشكل صحيح)

1️⃣ البوابة الموحدة والـ Headers الأمنية

الملف: backend/core/bootstrap.php

✓ جميع headers الأمان مُفعّلة:

  • Strict-Transport-Security: max-age=31536000 (HSTS)
  • X-Frame-Options: DENY (منع ClickJacking)
  • X-Content-Type-Options: nosniff (منع MIME sniffing)
  • Content-Security-Policy: default-src 'self' (صارم جداً)
  • Referrer-Policy: strict-origin-when-cross-origin
  • Permissions-Policy: geolocation=(), microphone=(), camera=()
  • حذف X-Powered-By (إخفاء تكنولوجيا الخادم)

✓ عرض الأخطاء مُطفأ في الإنتاج، مربوط بـ APP_DEBUG
✓ لا تسريب لـ stack traces أو معلومات النظام


2️⃣ نظام المصادقة JWT — قوي ومتعدد الطبقات

الملف: backend/core/Auth/JwtService.php

الميزات الأمنية:

الميزة التفاصيل
JTI + Blacklist كل توكن له معرّف فريد (JTI) في Redis لمنع التكرار والإلغاء
Device Fingerprint ربط الجهاز عبر sha256(fingerprint + pepper) — يمنع سرقة التوكن بين أجهزة
Refresh Tokens منفصلة ومخزنة بآمان في Redis (30 يوم)
HMAC مشتق كل مستخدم يحصل على hash_hmac('sha256', userId, hmacSecret)
التحقق من Issuer تتحقق من أن مصدر التوكن (iss) صحيح
تقييد Registration Tokens توكنات التسجيل تعمل فقط على 37 endpoint محددة مسبقاً، ممنوعة من باقي الـ API
بصمة التطبيق للدور service: التحقق من توقيع التطبيق الرسمي

3️⃣ لا توجد ثغرات SQL Injection

الملفات: جميع الملفات الرئيسية

✓ جميع الاستعلامات تستخدم PDO Prepared Statements
✓ ATTR_EMULATE_PREPARES => false — يُجبر عل فصل البيانات عن الأوامر
✓ حتى الـ WHERE الديناميكي في login.php يبني placeholders:

$conditions[] = "passengers.phone = :phone";
$params[':phone'] = $phone;
$stmt = $con->prepare($sql);
$stmt->execute($params); // آمن 100%

4️⃣ عدم وجود RCE (Remote Code Execution)

✓ لا يوجد eval() أو create_function()
✓ لا يوجد shell_exec() أو system() مع مدخلات المستخدم
✓ لا يوجد passthru() أو proc_open()
✓ لا SSRF: لا يوجد file_get_contents($userUrl) أو curl على URL من المستخدم


5️⃣ كلمات المرور و OTP محمية بـ Bcrypt

الملفات: core/Services/OtpService.php، جميع نقاط الدخول

✓ Bcrypt في كل مكان: password_hash(..., PASSWORD_BCRYPT)
✓ OTP محمي بـ Bcrypt في Redis:

  • تُخزّن مُشفّرة: password_hash($otp, PASSWORD_BCRYPT)
  • صلاحية 5 دقائق
  • حد أقصى 3 محاولات خاطئة
  • وقفل الحساب 30 دقيقة بعد الفشل

✓ حماية من Timing Attack: في loginAdmin.php:

$elapsed = microtime(true) - $startTime;
if ($elapsed < 0.1) usleep((int)((0.1 - $elapsed) * 1000000));

6️⃣ رفع الملفات آمن جداً

الملفات: core/helpers.php، upload_audio.php

✓ فحص MIME حقيقي عبر finfo (وليس الامتداد فقط):

$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime_type = $finfo->file($file['tmp_name']);
if (!in_array($mime_type, $allowed_mimes, true)) {
    return ['success' => false, 'error' => 'Invalid file type'];
}

✓ أسماء عشوائية: bin2hex(random_bytes(16)) — منع التخمين
✓ حدود حجم: 5MB أقصى
✓ صلاحيات آمنة: mkdir($targetDir, 0750, true) — قراءة للويب فقط


7️⃣ إدارة الأسرار والمفاتيح — نظيفة جداً

✓ تاريخ Git نظيف:

  • ملف .env لم يُرفع أبداً ويوجد في .gitignore
  • لا .enckey في Git
  • لا service-account.json في Git
  • لا أي مفاتيح (*.pem, *.key) في Git

✓ المفاتيح تُقرأ من:

  • ملفات خارج الـ webroot (/home/username/.enckey)
  • متغيرات البيئة (getenv('ENCRYPTION_KEY_PATH'))
  • لا توجد مفاتيح مُدرجة في الكود (hardcoded)

8️⃣ التشفير الحديث (AES-256-GCM)

الملف: core/Security/EncryptionHelper.php

✓ GCM للبيانات الثنائية:

  • IV عشوائي (12 بايت)
  • Authentication tag (تحقق من عدم التعديل)
  • معايير NIST

✓ توافقية CBC القديم للبيانات الموجودة (مرحلة انتقالية)


9️⃣ تحويلات المحفظة محمية من الانتحال

الملف: ride/driverWallet/transfer.php

✓ معرّف المُرسِل يأتي من JWT فقط:

if (empty($user_id) || $role !== 'driver') {
    exit; // ممنوع تمرير senderID من الطلب
}
$senderID = $user_id; // من JWT آمن

✓ حدود قصوى لكل دولة: Syria: 500، Egypt: 1000، Jordan: 15
✓ منع التحويل للنفس
✓ التحقق من وجود المستقبِل قبل المعالجة


🔟 CORS بقائمة سماح محددة

الملف: core/bootstrap.php

✓ قائمة CORS_ALLOWED_ORIGINS محدودة:

$allowedOrigins = array_map('trim', explode(',', getenv('CORS_ALLOWED_ORIGINS') ?: 'https://siromove.com,...'));

✓ Credentials لا تُرسل إلا للنطاقات المسموح بها
✓ لا wildcard مع credentials


1️⃣1️⃣ حماية من Email Header Injection

✓ تنظيف CR/LF في رؤوس البريد:

$from = str_replace(["\r", "\n", "\r\n"], '', $from);

⚠️ النقاط السلبية والتحديات الأمنية


🔴 مستوى عالي جداً (حرج) — يجب إصلاحها فوراً

H-1: تسريب الكود المصدري (Source Code Disclosure)

الملف: backend/check_server_code.php

<?php
header('Content-Type: text/plain');
echo file_get_contents(__DIR__ . '/auth/loginFromGooglePassenger.php');

الخطورة: 🔴🔴🔴

  • أي شخص يستطيع زيارة check_server_code.php والحصول على الكود المصدري الكامل
  • بدون مصادقة نهائياً — لا JWT، لا معرّفات، بلا حماية
  • يكشف منطق تسجيل الدخول، أسرار API، وآليات المصادقة

المثال:

GET /siro/check_server_code.php HTTP/1.1

✅ النتيجة: الكود المصدري كامل

الحل:

# احذف الملف من السيرفر تماماً
rm backend/check_server_code.php

H-2: تعطيل حماية Rate Limiting على التسجيل

الملف: backend/core/Auth/RateLimiter.php (سطور 48-51)

// Disable limit for register during debugging
if ($type === 'register') {
    return true;  // ⚠️ يسمح بطلبات غير محدودة
}

الخطورة: 🔴🔴🔴

  • كود تصحيح (debugging) بقي في الإنتاج
  • endpoint التسجيل بدون أي حد على الطلبات
  • هجوم Brute Force على أرقام الهاتف:
    • المهاجم يرسل 10,000 OTP لأرقام مختلفة
    • كل OTP تكلفة مالية (رسائل SMS)
    • إساءة استخدام رصيد SMS والموارد

مثال الهجوم:

# إرسال 10,000 طلب تسجيل بدون تأخير
for i in {1..10000}; do
  curl -X POST https://api.siromove.com/auth/signup \
    -d "phone=962791${i}&country=jordan"
done

الحل: أزل الشرط من السطرين 48-51:

// قبل (خطأ):
if ($type === 'register') {
    return true;
}

// بعد (صحيح):
// (احذف هذا الشرط كله)

الحد الأصلي (3 محاولات/ساعة) كافٍ جداً.


H-3: عدم حماية الملفات الحساسة على مستوى الخادم

الملفات المعرّضة:

  • .env — قاعدة بيانات، مفاتيح API
  • .git/ — تاريخ كامل المشروع
  • logs/ — معلومات النظام والأخطاء
  • composer.json / composer.lock — نسخ المكتبات

الخطورة: 🔴🔴🔴

إذا كان document root للخادم هو backend/ مباشرة، قد يستطيع المهاجم:

  • تنزيل .env ← الحصول على جميع الأسرار (DB passwords, API keys)
  • نسخ repository من .git ← تحليل كل تغيير وكل سر في التاريخ
  • قراءة logs/ ← الحصول على أسماء جداول، أرقام مستخدمين، stack traces

الحل (Nginx):

location ~ /\. {
    deny all;
    access_log off;
    log_not_found off;
}

location ~ (composer\.json|composer\.lock) {
    deny all;
}

location /logs/ {
    deny all;
}

الحل (Apache):

أنشئ ملف .htaccess في مجلد backend/:

# منع الوصول للملفات المخفية
<FilesMatch "^\.">
    <IfModule mod_authz_core.c>
        Require all denied
    </IfModule>
    <IfModule !mod_authz_core.c>
        Order allow,deny
        Deny from all
    </IfModule>
</FilesMatch>

# منع الوصول للملفات الحساسة
<FilesMatch "(composer\.json|composer\.lock)$">
    <IfModule mod_authz_core.c>
        Require all denied
    </IfModule>
</FilesMatch>

# منع الوصول لمجلد logs
<Directory "*/logs">
    <IfModule mod_authz_core.c>
        Require all denied
    </IfModule>
</Directory>

# منع تنزيل ملفات PHP
<FilesMatch "\.php$">
    <IfModule mod_authz_core.c>
        Require all denied
    </IfModule>
</FilesMatch>

الأفضل: فصل Document Root

/var/www/siro/
├── public/           ← document root فقط
│   └── index.php
├── backend/          ← خارج الـ webroot
│   ├── core/
│   ├── auth/
│   └── .env

🟠 مستوى متوسط — يجب إصلاحها قريباً

M-1: التشفير CBC بـ IV ثابت (Deterministic Encryption)

الملف: core/Security/EncryptionHelper.php (سطر 24 و دالة encryptData)

$this->cbcIv = $cbcIv ?: getenv('initializationVector') ?: str_repeat('0', 16);
// ثم في encryptData:
$encrypted = openssl_encrypt($padded, self::ALGO_CBC, $this->key, 
                             OPENSSL_RAW_DATA, $this->cbcIv);

المشكلة:

  • نفس النص ينتج نفس الـ ciphertext دائماً
  • تسريب معلومات: إذا كنت تشفّر أرقام هواتف، هاتف "962791234567" دائماً ينتج نفس القيمة
  • لا توثيق (MAC): لا يوجد فحص عدم التعديل على البيانات

مثال الهجوم:

عميل 1 يشفّر "962791234567" → ينتج "abc123def456"
عميل 2 يشفّر "962791234567" → ينتج نفس "abc123def456" ❌
المهاجم يرى التطابق ويكتشف نفس الهاتف

الحل:

استخدم encryptBinary بدلاً منه (يستخدم GCM مع IV عشوائي):

// قبل (خطأ):
$encrypted = $encryptionHelper->encryptData($plainText);

// بعد (صحيح):
// تحويل النص إلى binary وتشفيره بـ GCM
$encrypted = $encryptionHelper->encryptBinary(
    mb_convert_encoding($plainText, 'UTF-8')
);

M-2: تعطيل التحقق من شهادة TLS (SSL Certificate Verification)

الملف: bot/cron_silent_push_inactive.php (سطر 88)

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

الخطورة: 🟠🟠

  • يرسل مفتاح Firebase الخاص بك إلى خادم FCM
  • بدون التحقق من أن الخادم هو فعلاً fcm.googleapis.com
  • قابل لهجمات MITM (Man in the Middle):
    • هاكر يعترض الاتصال
    • يقرأ مفتاح Firebase
    • يرسل إشعارات وهمية
┌─────────────┐     SSL DISABLED      ┌─────────────┐
│  Siro API   │────────────────────→  │   ATTACKER  │
└─────────────┘      No Verify         └─────────────┘
                                             ↓
                                  يقرأ مفتاح Firebase

الحل:

أزل السطر، أو اجعله true:

// قبل:
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

// بعد:
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
// أو احذفه (الافتراضي true)

ملاحظة إضافية: الملف يستخدم FCM Legacy (القديم). الـ backend/ride/firebase/send_fcm.php بالفعل يستخدم FCM HTTP v1 (الحديث)، فيُفضّل ترحيل جميع الاستدعاءات إليه.


M-3: فحص HMAC والـ Device Fingerprint اختياري

الملف: core/Auth/JwtService.php (سطور 220-241 و 244-265)

// Device Fingerprint اختياري:
if ($fpInToken === null || $fpHeader === null) {
    // يتم تسجيل خطأ فقط، لكن يمرر
    error_log("[SECURITY] Fingerprint missing");
    // ❌ لا يوجد exit — يستمر!
}

// HMAC اختياري:
$hmacHeader = $_SERVER['HTTP_X_HMAC_AUTH'] ?? null;
if ($hmacHeader !== null) {  // ❌ اختياري!
    // يتحقق من HMAC فقط إذا موجود
}

المشكلة:

  • العميل يستطيع تخطّي الفحص بمجرد حذف الهيدر
  • لا حماية من replay attack: نفس الطلب يُقبل مراراً
  • timestamp و nonce في الكود لكن لا يُتحقق من حداثة الوقت ولا يُخزّن الـ nonce في Redis

مثال:

# الطلب الأول:
curl -H "X-HMAC-Auth: xyz" -H "X-Device-FP: abc" https://api.siromove.com/...

# الطلب الثاني (نفس الطلب):
curl https://api.siromove.com/...  # بدون الـ headers
# ✅ يُقبل لأن الفحص اختياري!

الحل:

اجعل HMAC إلزامياً للعمليات الحساسة:

// بدل من:
if ($hmacHeader !== null) { ... }

// اجعله:
if (in_array($decoded->role, ['service', 'driver'])) { // للأدوار الحساسة
    if ($hmacHeader === null) {
        self::abort(403, 'HMAC verification required');
    }
    // تحقق من HMAC هنا
}

وأضف فحص الـ timestamp:

$timestamp = (int)($_SERVER['HTTP_X_TIMESTAMP'] ?? 0);
$now = time();
if (abs($now - $timestamp) > 300) { // نافذة 5 دقائق
    self::abort(403, 'Request expired (timestamp mismatch)');
}

🟡 مستوى منخفض — تحصين وأفضليات

L-1: مقارنة التوكن بـ !== بدلاً من hash_equals

الملف: webhook_sms/webhook.php (سطر 17)

if ($authHeader !== 'Bearer ' . $secretKey) {  // ❌ تسريب توقيت
    exit;
}

// ✅ الصحيح:
if (!hash_equals($authHeader, 'Bearer ' . $secretKey)) {
    exit;
}

السبب: hash_equals تقارن بسرعة ثابتة (بدون تسريب توقيتي).


L-2: صلاحيات مجلد اللوج 0777 غير آمنة

الملف: نفس الملف (سطر 77)

@mkdir($logDir, 0777, true);  // ❌ قراءة وكتابة لكل شخص

الحل:

@mkdir($logDir, 0750, true);  // ✅ مالك يقرأ/يكتب/ينفذ، مجموعة تقرأ فقط

L-3: فحص الحد لكن تمرير القيمة الخام

الملف: ride/driverWallet/transfer.php (سطور 27-40)

$amountInt = (int)$amount;  // التحقق
if ($amountInt > $maxAmount) {
    exit; // حماية جيدة
}

// لكن تمرير:
'amount' => $amount  // ❌ تمرير النصي الخام

يمكن للعميل إرسال "15.99" أو "10abc"، والخادم سيتحقق من 10 لكن يرسل "10abc".

الحل:

'amount' => $amountInt  // ✅ تمرير القيمة المُتحقق منها

L-4: Prepared Statements مع قيم حرفية

بعض الملفات قد تستخدم قيماً حرفية في الـ WHERE، مثل:

// ✅ جيد:
$stmt = $con->prepare("SELECT * FROM rides WHERE status = ? AND driver_id = ?");
$stmt->execute(['completed', $driver_id]);

// ⚠️ احذر من:
// القيم الثابتة يجب تمريرها كـ parameters أيضاً

📋 جدول الإصلاحات الموصى بها

الأولوية الملف المشكلة الإصلاح المدة المتوقعة
🔴 عالي جداً check_server_code.php Source disclosure حذف الملف 5 دقائق
🔴 عالي جداً RateLimiter.php L48 تعطيل حماية register حذف الشرط الـ if 5 دقائق
🔴 عالي جداً All (خادم الويب) ملفات حساسة مكشوفة إضافة .htaccess / Nginx rules 15 دقيقة
🟠 متوسط EncryptionHelper.php CBC حتمي ترحيل إلى GCM ساعتين
🟠 متوسط cron_silent_push_inactive.php L88 تعطيل SSL verify إزالة السطر 5 دقائق
🟠 متوسط JwtService.php L244+ HMAC اختياري اجعله إلزامياً + تحقق توقيت ساعة واحدة
🟡 منخفض webhook_sms/webhook.php L17 مقارنة غير آمنة استخدم hash_equals 5 دقائق
🟡 منخفض Multiple صلاحيات غير آمنة غيّر إلى 0750 5 دقائق

✨ الخلاصة والتوصيات

ما تم إنجازه بشكل ممتاز

  1. ✅ No SQL Injection (Prepared Statements في كل مكان)
  2. ✅ No RCE (no eval/shell_exec/dangerous functions)
  3. ✅ Strong JWT + Device Fingerprint
  4. ✅ Bcrypt للأسرار و OTP
  5. ✅ Secure file uploads (MIME validation)
  6. ✅ Clean secrets history (no hardcoded keys in git)
  7. ✅ Security headers (HSTS, CSP, X-Frame-Options)

الخطوات الفورية (اليوم)

# 1. حذف ملف تسريب الكود
rm backend/check_server_code.php

# 2. إزالة تجاوز Rate Limiting
# عدّل backend/core/Auth/RateLimiter.php أسطر 48-51

# 3. إضافة حماية الخادم
# أنشئ backend/.htaccess أو عدّل Nginx config

خطوات متوسطة (الأسبوع القادم)

  • إضافة .htaccess / Nginx rules
  • حذف TLS verification disable
  • ترحيل من FCM Legacy إلى HTTP v1
  • جعل HMAC إلزامياً

خطوات طويلة الأمد

  • ترحيل من CBC إلى GCM (نقاط البيانات الجديدة)
  • إضافة فحص replay attack (timestamp + nonce)
  • مراجعة شاملة للـ rate limits

📞 ملاحظات ختامية

مستوى الأمان الحالي: 8.2/10
بعد تطبيق الإصلاحات العاجلة: 9.2/10
بعد تطبيق جميع التوصيات: 9.7/10

النظام آمن بشكل عام، لكن الإصلاحات الثلاث العاجلة حرجة ويجب تطبيقها قبل الإنتاج.


أُعد من قِبل: مدقق أمني مستقل
التاريخ: 8 يوليو 2026
للتواصل: hamzaayedpython@gmail.com