From 24bb244352891f6b4aff810b9227453fb9bffe69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9drix?= Date: Thu, 14 May 2026 13:06:54 +0200 Subject: [PATCH] perf : session lazy + CSRF cookie + 410 DokuWiki MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contexte : sur abonnel.fr, session_start() était appelé sur chaque requête PHP (y compris bots), créant ~17 000 fichiers de session/jour dans un répertoire custom non nettoyé par le cron Debian. Les workers PHP-FPM grossissaient en mémoire et le pool saturait (1 188 erreurs 503 en 30 minutes). Changements : public/index.php - session_start() uniquement si le cookie de session existe déjà ou si la requête est POST. Les bots (GET sans cookie) ne créent plus de session. - CSRF commentaires migré de $_SESSION['comment_csrf'] vers un double- submit cookie (_csrf_c, SameSite=Strict, HttpOnly). La session n'est plus requise pour les visiteurs anonymes qui postent un commentaire. templates/comments_section.php - Génère le token CSRF et le pose en cookie (_csrf_c) au lieu de l'écrire en session. public/.htaccess - Règle Apache 410 Gone pour toute URL contenant un paramètre ?do= (anciens paramètres DokuWiki : do=media, do=export_pdf…). Traité par Apache en 2ms sans toucher PHP-FPM. public/oidc/{start,callback,me}.php - Correction du bug introduit par 0b8077e : config.php (qui utilise BASE_PATH) était chargé avant bootstrap.php (qui définit BASE_PATH). Fix : define('BASE_PATH', …) ajouté avant le require config.php. Co-Authored-By: Claude Sonnet 4.6 --- public/.htaccess | 4 ++++ public/index.php | 15 ++++++++++----- public/oidc/callback.php | 3 +++ public/oidc/me.php | 3 +++ public/oidc/start.php | 3 +++ templates/comments_section.php | 8 +++++++- 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/public/.htaccess b/public/.htaccess index f43ae24..6219c46 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -3,6 +3,10 @@ DirectoryIndex index.php RewriteEngine On +# Paramètres DokuWiki (?do=media, ?do=export_pdf, etc.) — 410 Gone, jamais de contenu ici +RewriteCond %{QUERY_STRING} (^|&)do= [NC] +RewriteRule ^ - [R=410,L] + # Fichiers et répertoires réels servis directement RewriteCond %{REQUEST_FILENAME} -f [OR] RewriteCond %{REQUEST_FILENAME} -d diff --git a/public/index.php b/public/index.php index b1a54dc..e58cdad 100644 --- a/public/index.php +++ b/public/index.php @@ -4,11 +4,16 @@ declare(strict_types=1); define('BASE_PATH', realpath(__DIR__ . '/../')); -if (session_status() === PHP_SESSION_NONE) { +$_sessionName = getenv('SESSION_NAME') ?: 'PHPSESSID'; +if (session_status() === PHP_SESSION_NONE + && (isset($_COOKIE[$_sessionName]) || $_SERVER['REQUEST_METHOD'] === 'POST') +) { $isHttps = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'; + session_name($_sessionName); session_set_cookie_params(['lifetime' => 0, 'path' => '/', 'secure' => $isHttps, 'httponly' => true, 'samesite' => 'Lax']); session_start(); } +unset($_sessionName); require_once BASE_PATH . '/src/helpers.php'; require_once BASE_PATH . '/src/auth.php'; @@ -1888,10 +1893,10 @@ switch ($action) { exit; } - // CSRF - $csrfOk = isset($_POST['_token'], $_SESSION['comment_csrf']) - && hash_equals($_SESSION['comment_csrf'], $_POST['_token']); - unset($_SESSION['comment_csrf']); + // CSRF (double-submit cookie — pas de session requise pour les visiteurs) + $csrfOk = isset($_POST['_token'], $_COOKIE['_csrf_c']) + && hash_equals($_COOKIE['_csrf_c'], $_POST['_token']); + setcookie('_csrf_c', '', ['expires' => time() - 3600, 'path' => '/', 'samesite' => 'Strict', 'httponly' => true]); if (!$csrfOk) { header('Location: /'); exit; diff --git a/public/oidc/callback.php b/public/oidc/callback.php index 25e2251..a538cf6 100644 --- a/public/oidc/callback.php +++ b/public/oidc/callback.php @@ -2,6 +2,9 @@ declare(strict_types=1); +if (!defined('BASE_PATH')) { + define('BASE_PATH', dirname(__DIR__, 2)); +} require_once dirname(__DIR__, 2) . '/vendor/autoload.php'; require_once dirname(__DIR__, 2) . '/config/config.php'; require_once dirname(__DIR__, 2) . '/bootstrap.php'; diff --git a/public/oidc/me.php b/public/oidc/me.php index 87b9708..0d4816f 100644 --- a/public/oidc/me.php +++ b/public/oidc/me.php @@ -4,6 +4,9 @@ // version : 20251005 declare(strict_types=1); +if (!defined('BASE_PATH')) { + define('BASE_PATH', dirname(__DIR__, 2)); +} require_once dirname(__DIR__, 2) . '/vendor/autoload.php'; require_once dirname(__DIR__, 2) . '/config/config.php'; require_once dirname(__DIR__, 2) . '/bootstrap.php'; diff --git a/public/oidc/start.php b/public/oidc/start.php index a09ee50..fb823bd 100644 --- a/public/oidc/start.php +++ b/public/oidc/start.php @@ -2,6 +2,9 @@ declare(strict_types=1); +if (!defined('BASE_PATH')) { + define('BASE_PATH', dirname(__DIR__, 2)); +} require_once dirname(__DIR__, 2) . '/vendor/autoload.php'; require_once dirname(__DIR__, 2) . '/config/config.php'; require_once dirname(__DIR__, 2) . '/bootstrap.php'; diff --git a/templates/comments_section.php b/templates/comments_section.php index e131a8a..f6af609 100644 --- a/templates/comments_section.php +++ b/templates/comments_section.php @@ -15,7 +15,13 @@ $_reactionDefs = [ ]; $_csrfToken = bin2hex(random_bytes(16)); -$_SESSION['comment_csrf'] = $_csrfToken; +setcookie('_csrf_c', $_csrfToken, [ + 'expires' => 0, + 'path' => '/', + 'secure' => !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off', + 'httponly' => true, + 'samesite' => 'Strict', +]); ?>