diff --git a/CHANGELOG.md b/CHANGELOG.md index 976f466..68f5901 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Updated robthree/twofactorauth to v3 and endroid/qr-code to v5 - Updated TOTP integration (namespace, enum, QR provider) accordingly - Updated docker image from 22.04 to 24.04 +- Ensure consistent session behavior --- diff --git a/auth.php b/auth.php index 17e1ca5..fce81a0 100644 --- a/auth.php +++ b/auth.php @@ -5,7 +5,6 @@ require_once 'config.php'; use RobThree\Auth\Algorithm; use RobThree\Auth\Providers\Qr\GoogleChartsQrCodeProvider; -// Only send the Content-Type header; CORS and related headers are handled via .htaccess. header('Content-Type: application/json'); // Global exception handler: logs errors and returns a generic error message. @@ -180,7 +179,7 @@ if (!$username || !$password) { exit(); } -if (!preg_match('/^[A-Za-z0-9_\- ]+$/', $username)) { +if (!preg_match(REGEX_USER, $username)) { http_response_code(400); echo json_encode(["error" => "Invalid username format. Only letters, numbers, underscores, dashes, and spaces are allowed."]); exit(); diff --git a/config.php b/config.php index 5a522c1..44544e8 100644 --- a/config.php +++ b/config.php @@ -1,5 +1,20 @@ \ No newline at end of file diff --git a/downloadSharedFile.php b/downloadSharedFile.php index 75b8f18..8a77dc5 100644 --- a/downloadSharedFile.php +++ b/downloadSharedFile.php @@ -79,10 +79,6 @@ if (in_array($ext, ['jpg','jpeg','png','gif','bmp','webp','svg','ico'])) { header('Content-Disposition: attachment; filename="' . basename($realFilePath) . '"'); } -// Disable caching. -header("Cache-Control: no-store, no-cache, must-revalidate"); -header("Pragma: no-cache"); - // Read and output the file. readfile($realFilePath); exit; diff --git a/getFileList.php b/getFileList.php index 6f4674f..40e4317 100644 --- a/getFileList.php +++ b/getFileList.php @@ -1,8 +1,5 @@ { if (!response.ok) { @@ -21,11 +22,9 @@ function loadCsrfTokenWithRetry(retries = 3, delay = 1000) { return response.json(); }) .then(data => { - // Set global variables. window.csrfToken = data.csrf_token; window.SHARE_URL = data.share_url; - - // Update (or create) the CSRF meta tag. + let metaCSRF = document.querySelector('meta[name="csrf-token"]'); if (!metaCSRF) { metaCSRF = document.createElement('meta'); @@ -34,7 +33,6 @@ function loadCsrfTokenWithRetry(retries = 3, delay = 1000) { } metaCSRF.setAttribute('content', data.csrf_token); - // Update (or create) the share URL meta tag. let metaShare = document.querySelector('meta[name="share-url"]'); if (!metaShare) { metaShare = document.createElement('meta'); @@ -44,15 +42,6 @@ function loadCsrfTokenWithRetry(retries = 3, delay = 1000) { metaShare.setAttribute('content', data.share_url); return data; - }) - .catch(error => { - if (retries > 0) { - console.warn(`CSRF token load failed. Retrying in ${delay}ms... (${retries} retries left)`, error); - return new Promise(resolve => setTimeout(resolve, delay)) - .then(() => loadCsrfTokenWithRetry(retries - 1, delay * 2)); - } - console.error("Failed to load CSRF token after retries.", error); - throw error; }); } @@ -78,7 +67,7 @@ document.addEventListener("DOMContentLoaded", function () { // Apply the translations to update the UI applyTranslations(); // First, load the CSRF token (with retry). - loadCsrfTokenWithRetry().then(() => { + loadCsrfToken().then(() => { // Once CSRF token is loaded, initialize authentication. initAuth(); diff --git a/moveFiles.php b/moveFiles.php index 65dc9ea..9874740 100644 --- a/moveFiles.php +++ b/moveFiles.php @@ -1,9 +1,6 @@ "Invalid CSRF token."]); - http_response_code(403); - exit; +$headers = array_change_key_case(getallheaders(), CASE_LOWER); +$csrfHeader = isset($headers['x-csrf-token']) ? trim($headers['x-csrf-token']) : ''; + +if (!isset($_SESSION['csrf_token']) || $csrfHeader !== $_SESSION['csrf_token']) { + respond('error', 403, 'Invalid CSRF token'); } $username = $_SESSION['username'] ?? ''; diff --git a/totp_disable.php b/totp_disable.php index 7197db1..e648ecc 100644 --- a/totp_disable.php +++ b/totp_disable.php @@ -11,11 +11,11 @@ if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) { } // Verify CSRF token from request headers. -$csrfHeader = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? ''; +$headers = array_change_key_case(getallheaders(), CASE_LOWER); +$csrfHeader = isset($headers['x-csrf-token']) ? trim($headers['x-csrf-token']) : ''; + if (!isset($_SESSION['csrf_token']) || $csrfHeader !== $_SESSION['csrf_token']) { - http_response_code(403); - echo json_encode(["error" => "Invalid CSRF token"]); - exit; + respond('error', 403, 'Invalid CSRF token'); } header('Content-Type: application/json'); diff --git a/totp_recover.php b/totp_recover.php index 387be11..4a9bd05 100644 --- a/totp_recover.php +++ b/totp_recover.php @@ -13,11 +13,11 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST') { } // ——— 2) CSRF check ——— -if (empty($_SERVER['HTTP_X_CSRF_TOKEN']) - || $_SERVER['HTTP_X_CSRF_TOKEN'] !== ($_SESSION['csrf_token'] ?? '')) { - http_response_code(403); - error_log("Invalid CSRF token on recovery for IP {$_SERVER['REMOTE_ADDR']}"); - exit(json_encode(['status'=>'error','message'=>'Invalid CSRF token'])); +$headers = array_change_key_case(getallheaders(), CASE_LOWER); +$csrfHeader = isset($headers['x-csrf-token']) ? trim($headers['x-csrf-token']) : ''; + +if (!isset($_SESSION['csrf_token']) || $csrfHeader !== $_SESSION['csrf_token']) { + respond('error', 403, 'Invalid CSRF token'); } // ——— 3) Identify user to recover ——— diff --git a/totp_saveCode.php b/totp_saveCode.php index ba37ffc..e2058a9 100644 --- a/totp_saveCode.php +++ b/totp_saveCode.php @@ -13,11 +13,11 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST') { } // 2) CSRF check -if (empty($_SERVER['HTTP_X_CSRF_TOKEN']) - || $_SERVER['HTTP_X_CSRF_TOKEN'] !== ($_SESSION['csrf_token'] ?? '')) { - http_response_code(403); - error_log("totp_saveCode: invalid CSRF token from IP {$_SERVER['REMOTE_ADDR']}"); - exit(json_encode(['status'=>'error','message'=>'Invalid CSRF token'])); +$headers = array_change_key_case(getallheaders(), CASE_LOWER); +$csrfHeader = isset($headers['x-csrf-token']) ? trim($headers['x-csrf-token']) : ''; + +if (!isset($_SESSION['csrf_token']) || $csrfHeader !== $_SESSION['csrf_token']) { + respond('error', 403, 'Invalid CSRF token'); } // 3) Must be logged in diff --git a/totp_setup.php b/totp_setup.php index 191748a..126524a 100644 --- a/totp_setup.php +++ b/totp_setup.php @@ -14,10 +14,11 @@ if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) { exit; } -// Verify CSRF token provided as a GET parameter. -if (!isset($_GET['csrf']) || $_GET['csrf'] !== $_SESSION['csrf_token']) { - http_response_code(403); - exit; +$headers = array_change_key_case(getallheaders(), CASE_LOWER); +$csrfHeader = isset($headers['x-csrf-token']) ? trim($headers['x-csrf-token']) : ''; + +if (!isset($_SESSION['csrf_token']) || $csrfHeader !== $_SESSION['csrf_token']) { + respond('error', 403, 'Invalid CSRF token'); } $username = $_SESSION['username'] ?? ''; diff --git a/totp_verify.php b/totp_verify.php index d98002d..979f87d 100644 --- a/totp_verify.php +++ b/totp_verify.php @@ -57,8 +57,9 @@ try { respond('error', 403, 'Not authenticated'); } - // CSRF check - $csrfHeader = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? ''; + $headers = array_change_key_case(getallheaders(), CASE_LOWER); + $csrfHeader = isset($headers['x-csrf-token']) ? trim($headers['x-csrf-token']) : ''; + if (!isset($_SESSION['csrf_token']) || $csrfHeader !== $_SESSION['csrf_token']) { respond('error', 403, 'Invalid CSRF token'); } @@ -133,7 +134,7 @@ try { 30, // period in seconds Algorithm::Sha1 // Correct enum case name from your enum ); - + if (!$tfa->verifyCode($totpSecret, $code)) { $_SESSION['totp_failures']++; respond('error', 400, 'Invalid TOTP code');