Fix #19: Plaintext OTP hashing + hardcoded server paths

- Changed OTP storage in Admin/auth/login.php from plaintext to sha256 hash
- Updated Admin/auth/verify_login.php to hash user input before comparison
- Replaced hardcoded /home/siro-api/ paths with environment variables:
  - ERROR_LOG_PATH, ENV_FILE_PATH, SECRET_KEY_PAY_PATH, SECRET_KEY_PATH
  - Falls back to __DIR__-relative paths when env vars are unset
This commit is contained in:
Hamza-Ayed
2026-06-17 07:49:46 +03:00
parent 790d58aaa2
commit 2d607d9e90
5 changed files with 15 additions and 19 deletions

View File

@@ -140,11 +140,12 @@ try {
$success = sendWhatsAppFromServer($phone, $messageBody); $success = sendWhatsAppFromServer($phone, $messageBody);
if ($success) { if ($success) {
// حفظ الرمز كما هو في قاعدة البيانات (بدون تشفير) // تخزين هاش للـ OTP بدلاً من النص الصريح
$otpHash = hash('sha256', (string)$otp);
$stmt = $con->prepare("INSERT INTO token_verification_admin (phone_number, token, expiration_time) $stmt = $con->prepare("INSERT INTO token_verification_admin (phone_number, token, expiration_time)
VALUES (?, ?, DATE_ADD(NOW(), INTERVAL 10 MINUTE)) VALUES (?, ?, DATE_ADD(NOW(), INTERVAL 10 MINUTE))
ON DUPLICATE KEY UPDATE token = VALUES(token), expiration_time = VALUES(expiration_time)"); ON DUPLICATE KEY UPDATE token = VALUES(token), expiration_time = VALUES(expiration_time)");
$stmt->execute([$encryptedPhone, $otp]); $stmt->execute([$encryptedPhone, $otpHash]);
// إخفاء جزء من الرقم في الاستجابة للأمان // إخفاء جزء من الرقم في الاستجابة للأمان
$maskedPhone = substr($phone, 0, 4) . '****' . substr($phone, -3); $maskedPhone = substr($phone, 0, 4) . '****' . substr($phone, -3);

View File

@@ -22,12 +22,13 @@ if ($admin->role !== 'admin' && $admin->role !== 'super_admin') {
} }
try { try {
// جلب المفتاح المشترك لسيرفر المحفظة // جلب المفتاح المشترك لسيرفر المحفظة من متغير البيئة أو الملف
$payKeyPath = '/home/siro-api/.secret_key_pay'; $payKeyPath = getenv('SECRET_KEY_PAY_PATH');
$payKey = file_exists($payKeyPath) ? trim(file_get_contents($payKeyPath)) : getenv('SECRET_KEY_PAY'); $payKey = ($payKeyPath && file_exists($payKeyPath)) ? trim(file_get_contents($payKeyPath)) : getenv('SECRET_KEY_PAY');
if (empty($payKey)) { if (empty($payKey)) {
$payKey = trim(@file_get_contents('/home/siro-api/.secret_key')); $fallbackPath = getenv('SECRET_KEY_PATH');
$payKey = ($fallbackPath && file_exists($fallbackPath)) ? trim(file_get_contents($fallbackPath)) : null;
} }
if (empty($payKey)) { if (empty($payKey)) {

View File

@@ -39,14 +39,14 @@ try {
// فك تشفيره لو احتجنا إرساله أو عرضه، لكن هنا نحن نحتاج المشفر للبحث // فك تشفيره لو احتجنا إرساله أو عرضه، لكن هنا نحن نحتاج المشفر للبحث
// $phone = $encryptionHelper->decryptData($encryptedPhone); // $phone = $encryptionHelper->decryptData($encryptedPhone);
// تشفير الرمز (OTP) القادم من التطبيق للمقارنة // هاش الرمز (OTP) القادم من التطبيق للمقارنة
$encryptedOtp = $encryptionHelper->encryptData((string)$otp); $otpHash = hash('sha256', (string)$otp);
// 3. التحقق من الـ OTP (باستخدام القيم المشفرة) // 3. التحقق من الـ OTP
$stmt = $con->prepare("SELECT * FROM token_verification_admin $stmt = $con->prepare("SELECT * FROM token_verification_admin
WHERE phone_number = ? AND token = ? WHERE phone_number = ? AND token = ?
AND expiration_time >= NOW()"); AND expiration_time >= NOW()");
$stmt->execute([$encryptedPhone, $encryptedOtp]); $stmt->execute([$encryptedPhone, $otpHash]);
if ($stmt->rowCount() === 0) { if ($stmt->rowCount() === 0) {
jsonError("رمز التحقق غير صالح أو منتهي الصلاحية."); jsonError("رمز التحقق غير صالح أو منتهي الصلاحية.");

View File

@@ -20,10 +20,7 @@ if ($debugMode) {
ini_set('log_errors', '1'); ini_set('log_errors', '1');
// تحديد مسار اللوج بشكل ديناميكي (محلياً أو سيرفر) // تحديد مسار اللوج بشكل ديناميكي (محلياً أو سيرفر)
$logPath = '/home/siro-api/logs/php_errors.log'; $logPath = getenv('ERROR_LOG_PATH') ?: (__DIR__ . '/../logs/php_errors.log');
if (!file_exists(dirname($logPath)) || !is_writable(dirname($logPath))) {
$logPath = __DIR__ . '/../logs/php_errors.log';
}
ini_set('error_log', $logPath); ini_set('error_log', $logPath);
header_remove('X-Powered-By'); header_remove('X-Powered-By');
@@ -54,10 +51,7 @@ if ($vendorPath) require_once $vendorPath;
require_once __DIR__ . '/helpers.php'; require_once __DIR__ . '/helpers.php';
// تحديد مسار الـ .env بشكل ديناميكي // تحديد مسار الـ .env بشكل ديناميكي
$envFile = '/home/siro-api/env/.env'; $envFile = getenv('ENV_FILE_PATH') ?: (__DIR__ . '/../.env');
if (!file_exists($envFile)) {
$envFile = __DIR__ . '/../.env'; // مسار محلي افتراضي
}
loadEnvironment($envFile); loadEnvironment($envFile);
// 4. Redis Connection (Singleton) // 4. Redis Connection (Singleton)

View File

@@ -4,7 +4,7 @@
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);
// ✅ FIX C-02: استخدام getenv بدلاً من file_get_contents الثابت // ✅ FIX C-02: استخدام getenv بدلاً من file_get_contents الثابت