From 9764e519cf2d6a6da8a56daa70dd2dee1b94de61 Mon Sep 17 00:00:00 2001 From: TeaTinyTool Date: Sat, 13 Jun 2026 17:25:56 +0200 Subject: [PATCH] Fix editor save handling for expired sessions Prevent the editor from showing `Saved Successfully` when the session has expired or the CSRF token is stale. Expired save requests now return an explicit error response instead of the login page. Stale CSRF tokens are refreshed without auto-saving, so the user has to click save again intentionally before the file is written. --- tinyfilemanager.php | 54 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/tinyfilemanager.php b/tinyfilemanager.php index 9dfa94cf..71846e27 100644 --- a/tinyfilemanager.php +++ b/tinyfilemanager.php @@ -274,6 +274,15 @@ function session_error_handling_function($code, $msg, $file, $line) } } +// Parse JSON AJAX save requests before checking authentication +$contentType = isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : ''; +if (stripos($contentType, 'application/json') !== false) { + $jsonPost = json_decode(file_get_contents('php://input'), true); + if (is_array($jsonPost) && !empty($jsonPost['ajax']) && isset($jsonPost['type']) && $jsonPost['type'] === 'save') { + $_POST = $jsonPost; + } +} + if (empty($auth_users)) { $use_auth = false; } @@ -367,6 +376,12 @@ function getClientIP() } else { // Form unset($_SESSION[FM_SESSION_ID]['logged']); + if (!empty($_POST['ajax']) && isset($_POST['type']) && $_POST['type'] === 'save') { + header('HTTP/1.1 401 Unauthorized'); + header('Content-Type: text/plain; charset=UTF-8'); + header('Cache-Control: no-store'); + die("Session expired. The file was not saved. Log in again in another tab, then click Save again."); + } fm_show_header_login(); ?>
@@ -462,10 +477,6 @@ function getClientIP() // clean path $p = fm_clean_path($p); -// for ajax request - save -$input = file_get_contents('php://input'); -$_POST = (strpos($input, 'ajax') != FALSE && strpos($input, 'save') != FALSE) ? json_decode($input, true) : $_POST; - // instead globals vars define('FM_PATH', $p); define('FM_USE_AUTH', $use_auth); @@ -482,6 +493,13 @@ function getClientIP() // Handle all AJAX Request if ((isset($_SESSION[FM_SESSION_ID]['logged'], $auth_users[$_SESSION[FM_SESSION_ID]['logged']]) || !FM_USE_AUTH) && isset($_POST['ajax'], $_POST['token'])) { if (!verifyToken($_POST['token'])) { + if (isset($_POST['type']) && $_POST['type'] === 'save') { + header('HTTP/1.1 409 Conflict'); + header('Content-Type: text/plain; charset=UTF-8'); + header('Cache-Control: no-store'); + header('X-CSRF-Token: ' . $_SESSION['token']); + die("Invalid Token."); + } header('HTTP/1.0 401 Unauthorized'); die("Invalid Token."); } @@ -528,7 +546,10 @@ function getClientIP() header("HTTP/1.1 500 Internal Server Error"); die("Could Not Write File! - Check Permissions / Ownership"); } - die(true); + header('X-TFM-Save-Success: 1'); + header('Content-Type: text/plain; charset=UTF-8'); + header('Cache-Control: no-store'); + die('1'); } // backup files @@ -4942,16 +4963,31 @@ function edit_save(e, t) { url: window.location, data: JSON.stringify(data), contentType: "application/json; charset=utf-8", - success: function(mes) { - toast(""); - window.onbeforeunload = function() { - return + success: function(mes, textStatus, xhr) { + var saveConfirmed = xhr.getResponseHeader("X-TFM-Save-Success") === "1" || String(mes).trim() === "1"; + if (saveConfirmed) { + toast(""); + window.onbeforeunload = function() { + return + } + } else { + alert("The file was not saved. The server returned an unexpected response:\n\n" + String(mes).trim().substring(0, 500)); } }, failure: function(mes) { toast(""); }, error: function(mes) { + var refreshedToken = mes.getResponseHeader("X-CSRF-Token"); + if (mes.status === 409 && refreshedToken) { + window.csrf = refreshedToken; + alert("Your session was renewed. The file was not saved. The security token has been refreshed. Click Save again to confirm."); + return; + } + if (mes.status === 401) { + alert("Session expired. The file was not saved. Log in again in another tab, then click Save again."); + return; + } toast(`

${mes.responseText}

`); } });