Turnstile > Add site * (gratuit, aucune carte bancaire requise). */ declare(strict_types=1); require_once __DIR__ . '/admin_auth.php'; require_once __DIR__ . '/../lib/db.php'; // Fichier de config NON versionne (a creer manuellement sur le // serveur, en dehors du depot Git) : voir config/turnstile.example.php $turnstileConfigPath = __DIR__ . '/../config/turnstile.php'; $turnstileEnabled = false; if (is_file($turnstileConfigPath)) { require_once $turnstileConfigPath; $turnstileEnabled = defined('TURNSTILE_ENABLED') && TURNSTILE_ENABLED && defined('TURNSTILE_SITE_KEY') && TURNSTILE_SITE_KEY !== '' && defined('TURNSTILE_SECRET_KEY') && TURNSTILE_SECRET_KEY !== ''; if ($turnstileEnabled && !function_exists('curl_init')) { error_log('[login] Turnstile configure mais extension php-curl absente : CAPTCHA desactive automatiquement.'); $turnstileEnabled = false; } } if (!defined('TURNSTILE_FAIL_CLOSED')) { define('TURNSTILE_FAIL_CLOSED', false); } $db = pdo(); $error = null; $timeout = isset($_GET['timeout']); /** * Verifie le jeton Turnstile aupres de l'API Cloudflare. * * Retourne true si le jeton est valide. Si l'appel reseau vers * Cloudflare echoue (timeout, DNS, panne), le comportement depend de * TURNSTILE_FAIL_CLOSED : * - false (par defaut) : on laisse passer la tentative (le login * reste protege par le mot de passe et le rate limiting SQL), pour * ne jamais bloquer l'acces admin a cause d'une panne Cloudflare * ou d'une coupure Internet sortante temporaire sur la VM. * - true : on bloque la tentative tant que Turnstile n'a pas * confirme, pour un site expose publiquement sans acces de secours * alternatif. */ function verifyTurnstileToken(string $token, string $remoteIp): bool { if ($token === '') { return false; } $ch = curl_init('https://challenges.cloudflare.com/turnstile/v0/siteverify'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5, CURLOPT_POSTFIELDS => http_build_query([ 'secret' => TURNSTILE_SECRET_KEY, 'response' => $token, 'remoteip' => $remoteIp, ]), ]); $response = curl_exec($ch); $curlError = curl_errno($ch); curl_close($ch); if ($curlError !== 0 || $response === false) { error_log('[login] Turnstile injoignable (curl errno ' . $curlError . '). Fail-closed=' . (TURNSTILE_FAIL_CLOSED ? 'oui' : 'non') . '.'); return !TURNSTILE_FAIL_CLOSED; } $decoded = json_decode($response, true); return is_array($decoded) && !empty($decoded['success']); } if ($_SERVER['REQUEST_METHOD'] === 'POST') { require_csrf_token(); $username = trim((string)($_POST['username'] ?? '')); $password = (string)($_POST['password'] ?? ''); $turnstileVerified = true; // reste a true si Turnstile est desactive if ($turnstileEnabled) { $turnstileToken = (string)($_POST['cf-turnstile-response'] ?? ''); $turnstileVerified = verifyTurnstileToken($turnstileToken, client_ip_address()); } if (!$turnstileVerified) { $error = 'Verification anti-robot echouee. Reessayez.'; } elseif ($username === '') { $error = 'Identifiants incorrects.'; } elseif (is_login_locked_out($db, $username)) { $error = 'Trop de tentatives echouees. Reessayez dans quelques minutes.'; } else { if (attempt_admin_login($db, $username, $password)) { record_login_attempt($db, $username, true, $turnstileVerified); header('Location: index.php'); exit; } record_login_attempt($db, $username, false, $turnstileVerified); $error = 'Identifiants incorrects.'; } } ?>
Session expiree, veuillez vous reconnecter.
= htmlspecialchars($error, ENT_QUOTES, 'UTF-8') ?>