Update: 2026-06-16 01:17:28
This commit is contained in:
@@ -36,18 +36,18 @@ class JwtService
|
||||
|
||||
public function __construct(?Redis $redis = null)
|
||||
{
|
||||
$this->secretKey = trim(file_get_contents('/home/siro-api/.secret_key'));
|
||||
// ✅ FIX C-02: استخدام getenv بدلاً من file_get_contents الثابت
|
||||
$keyPath = getenv('JWT_SECRET_KEY_PATH');
|
||||
if ($keyPath && file_exists($keyPath)) {
|
||||
$this->secretKey = trim(file_get_contents($keyPath));
|
||||
} else {
|
||||
$this->secretKey = getenv('JWT_SECRET_KEY') ?: '';
|
||||
}
|
||||
|
||||
$this->hmacSecret = getenv('SECRET_KEY_HMAC') ?: '';
|
||||
$this->fpPepper = getenv('FP_PEPPER') ?: '';
|
||||
$this->issuer = (string)(getenv('APP_ISSUER') ?: '');
|
||||
$this->redis = $redis;
|
||||
|
||||
// Debugging fpPepper
|
||||
if (empty($this->fpPepper)) {
|
||||
error_log("[JWT_DEBUG] fpPepper is EMPTY in constructor");
|
||||
} else {
|
||||
error_log("[JWT_DEBUG] fpPepper is SET (length: " . strlen($this->fpPepper) . ")");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -144,19 +144,8 @@ class JwtService
|
||||
} catch (ExpiredException $e) {
|
||||
self::abort(401, 'Token expired');
|
||||
} catch (SignatureInvalidException $e) {
|
||||
// محاولة فك التشفير بمفتاح المحفظة (Wallet secret fallback)
|
||||
$payKeyPath = '/home/siro-api/.secret_key_pay';
|
||||
$payKey = file_exists($payKeyPath) ? trim(file_get_contents($payKeyPath)) : '';
|
||||
|
||||
if ($payKey) {
|
||||
try {
|
||||
$decoded = JWT::decode($token, new Key($payKey, self::ALGO));
|
||||
} catch (Exception $e2) {
|
||||
self::abort(401, 'Invalid token signature');
|
||||
}
|
||||
} else {
|
||||
self::abort(401, 'Invalid token signature');
|
||||
}
|
||||
// ممنوع استخدام أي مفتاح آخر - مفتاح JWT واحد فقط
|
||||
self::abort(401, 'Invalid token signature');
|
||||
} catch (BeforeValidException $e) {
|
||||
self::abort(401, 'Token not yet valid');
|
||||
} catch (Exception $e) {
|
||||
@@ -262,12 +251,11 @@ class JwtService
|
||||
$expectedHmac = hash_hmac('sha256', $payloadToSign, $userSecret);
|
||||
|
||||
if (!hash_equals($expectedHmac, $hmacHeader)) {
|
||||
$debugMsg = "User: $userId | Expected: $expectedHmac | Got: $hmacHeader | DerivedSecret: $userSecret | MasterSecret(4): " . substr($this->hmacSecret, 0, 4) . " | Body($bodyLen): '$body' | TS: '$timestamp' | Nonce: '$nonce'";
|
||||
$bodyLen = strlen($body);
|
||||
error_log("[SECURITY] HMAC mismatch | " . $debugMsg);
|
||||
// TEMPORARY: expose debug in response for diagnosis
|
||||
error_log("[SECURITY] HMAC mismatch | User: $userId | BodyLen: $bodyLen | TS: '$timestamp'");
|
||||
// ✅ FIX H-02: إزالة معلومات الـ Debug من الاستجابة
|
||||
http_response_code(403);
|
||||
echo json_encode(['error' => 'HMAC_DEBUG', 'debug' => $debugMsg]);
|
||||
echo json_encode(['error' => 'Request verification failed']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -288,7 +276,13 @@ class JwtService
|
||||
{
|
||||
$keyPath = getenv('INTERNAL_SOCKET_KEY_PATH');
|
||||
$sent = $_SERVER['HTTP_X_INTERNAL_KEY'] ?? '';
|
||||
$expected = (file_exists($keyPath) ? trim(file_get_contents($keyPath)) : '') ?: 'Siro_Secure_Bridge_Key_2026_@!socket';
|
||||
$expected = '';
|
||||
if ($keyPath && file_exists($keyPath)) {
|
||||
$expected = trim(file_get_contents($keyPath));
|
||||
}
|
||||
if (!$expected) {
|
||||
$expected = getenv('INTERNAL_SOCKET_KEY');
|
||||
}
|
||||
|
||||
if (!$expected || !hash_equals($expected, $sent)) {
|
||||
error_log('[SECURITY] Invalid internal key from: ' . ($_SERVER['REMOTE_ADDR'] ?? '?'));
|
||||
|
||||
@@ -8,7 +8,7 @@ declare(strict_types=1);
|
||||
|
||||
// 1. إعدادات الأخطاء والـ Headers الأساسية
|
||||
// اجعل القيمة true لتفعيل عرض الأخطاء (التطوير)، أو false لإخفائها (التشغيل الفعلي)
|
||||
$debugMode = true;
|
||||
$debugMode = getenv('APP_DEBUG') === 'true';
|
||||
|
||||
if ($debugMode) {
|
||||
error_reporting(E_ALL);
|
||||
@@ -26,10 +26,16 @@ if (!file_exists(dirname($logPath)) || !is_writable(dirname($logPath))) {
|
||||
}
|
||||
ini_set('error_log', $logPath);
|
||||
|
||||
header_remove('X-Powered-By');
|
||||
header('Content-Type: application/json; charset=UTF-8');
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
header('X-Frame-Options: DENY');
|
||||
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
|
||||
header("Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none'");
|
||||
header("Referrer-Policy: strict-origin-when-cross-origin");
|
||||
header("Permissions-Policy: geolocation=(), microphone=(), camera=()");
|
||||
header("X-XSS-Protection: 1; mode=block");
|
||||
|
||||
|
||||
// CORS (يجب تخصيصه في endpoints مخصصة إن لزم، لكن هذا افتراضي)
|
||||
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
|
||||
|
||||
@@ -76,7 +76,15 @@ function result(int $count): void
|
||||
}
|
||||
function sendEmail(string $from, string $to, string $title, string $body): void
|
||||
{
|
||||
$header = "From: $from\nCC: $from";
|
||||
$from = str_replace(["\r", "\n", "\r\n"], '', $from);
|
||||
$to = str_replace(["\r", "\n", "\r\n"], '', $to);
|
||||
$title = str_replace(["\r", "\n", "\r\n"], '', $title);
|
||||
|
||||
$header = "From: $from\r\n";
|
||||
$header .= "Reply-To: $from\r\n";
|
||||
$header .= "MIME-Version: 1.0\r\n";
|
||||
$header .= "Content-Type: text/html; charset=UTF-8\r\n";
|
||||
|
||||
mail($to, $title, $body, $header);
|
||||
}
|
||||
|
||||
@@ -155,7 +163,7 @@ function securityLog(string $message, array $context = []): void
|
||||
{
|
||||
$logDir = __DIR__ . '/../logs';
|
||||
if (!is_dir($logDir)) {
|
||||
@mkdir($logDir, 0777, true);
|
||||
@mkdir($logDir, 0750, true);
|
||||
}
|
||||
$entry = date('Y-m-d H:i:s') . ' [SECURITY] ' . $message;
|
||||
if ($context) $entry .= ' | ' . json_encode($context, JSON_UNESCAPED_UNICODE);
|
||||
@@ -166,7 +174,7 @@ function appLog(string $message, string $level = 'INFO'): void
|
||||
{
|
||||
$logDir = __DIR__ . '/../logs';
|
||||
if (!is_dir($logDir)) {
|
||||
@mkdir($logDir, 0777, true);
|
||||
@mkdir($logDir, 0750, true);
|
||||
}
|
||||
$entry = date('Y-m-d H:i:s') . " [$level] " . $message;
|
||||
@error_log($entry . PHP_EOL, 3, $logDir . '/app.log');
|
||||
@@ -176,7 +184,7 @@ function uploadLog(string $message, string $level = 'INFO', array $context = [])
|
||||
{
|
||||
$logDir = __DIR__ . '/../logs';
|
||||
if (!is_dir($logDir)) {
|
||||
@mkdir($logDir, 0777, true);
|
||||
@mkdir($logDir, 0750, true);
|
||||
}
|
||||
|
||||
if (!isset($context['ip'])) {
|
||||
@@ -212,3 +220,17 @@ function debugLog(string $message): void
|
||||
{
|
||||
appLog($message, 'DEBUG');
|
||||
}
|
||||
|
||||
function getInternalSocketKey(): string
|
||||
{
|
||||
$key = getenv('INTERNAL_SOCKET_KEY');
|
||||
if ($key) {
|
||||
return trim($key);
|
||||
}
|
||||
$path = getenv('INTERNAL_SOCKET_KEY_PATH') ?: '/home/siro-api/.internal_socket_key';
|
||||
if (file_exists($path)) {
|
||||
return trim((string)@file_get_contents($path));
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user