$payload Corps JSON déjà décodé. */ public static function assertPayload(array $payload): void { $expected = self::ensureToken(); $provided = (string) ($payload['csrf_token'] ?? ''); if ($provided === '' || !hash_equals($expected, $provided)) { // Un jeton CSRF absent ou faux est le plus souvent une session // expirée, parfois une tentative réelle : dans les deux cas la trace // a sa place dans le journal (V7.11). Journal::refus('CSRF_INVALIDE', 'Jeton de sécurité absent ou invalide.', [ 'contexte' => ['endpoint' => basename((string) ($_SERVER['SCRIPT_NAME'] ?? 'inconnu'))], ]); // send() termine le script : rien ne s'exécute après ce point. JsonResponse::send(403, ['error' => 'Jeton de sécurité invalide. Rechargez la page.']); } } }