185 lines
6.3 KiB
PHP
185 lines
6.3 KiB
PHP
<?php
|
|
|
|
namespace libraries;
|
|
|
|
/**
|
|
* Classe de validation Cloudflare Turnstile
|
|
*/
|
|
class TurnstileValidator
|
|
{
|
|
private $secretKey;
|
|
private $timeout;
|
|
|
|
/**
|
|
* Constructeur
|
|
* @param string $secretKey Clé secrète Turnstile (optionnel, utilise la constante par défaut)
|
|
* @param int $timeout Timeout en millisecondes (optionnel, défaut 10000ms)
|
|
*/
|
|
public function __construct($secretKey = null, $timeout = 10000)
|
|
{
|
|
$this->secretKey = $secretKey ?: _CRED_TURNSTILE_SECRET_KEY_;
|
|
$this->timeout = $timeout;
|
|
}
|
|
|
|
/**
|
|
* Valide un token Turnstile
|
|
*
|
|
* @param string $token Le token à valider
|
|
* @param string $remoteip L'adresse IP du client (optionnel)
|
|
* @param string $idempotencyKey Clé d'idempotence pour éviter les doublons (optionnel)
|
|
* @return array Résultat de la validation
|
|
*/
|
|
public function validate($token, $remoteip = null, $idempotencyKey = null)
|
|
{
|
|
// Validation des paramètres d'entrée
|
|
if (empty($token) || !is_string($token)) {
|
|
return [
|
|
'success' => false,
|
|
'error' => 'Token manquant ou invalide',
|
|
'error_codes' => ['missing-input-response']
|
|
];
|
|
}
|
|
|
|
if (strlen($token) > 2048) {
|
|
return [
|
|
'success' => false,
|
|
'error' => 'Token trop long',
|
|
'error_codes' => ['invalid-input-response']
|
|
];
|
|
}
|
|
|
|
// Préparation de la requête
|
|
$url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
|
|
|
|
$postData = [
|
|
'secret' => $this->secretKey,
|
|
'response' => $token
|
|
];
|
|
|
|
if (!empty($remoteip)) {
|
|
$postData['remoteip'] = $remoteip;
|
|
}
|
|
|
|
if (!empty($idempotencyKey)) {
|
|
$postData['idempotency_key'] = $idempotencyKey;
|
|
}
|
|
|
|
// Configuration cURL
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT_MS, $this->timeout);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'Content-Type: application/x-www-form-urlencoded'
|
|
]);
|
|
|
|
// Exécution de la requête
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
$curlError = curl_error($ch);
|
|
curl_close($ch);
|
|
|
|
// Gestion des erreurs cURL
|
|
if ($response === false) {
|
|
error_log('Turnstile validation cURL error: ' . $curlError);
|
|
return [
|
|
'success' => false,
|
|
'error' => 'Erreur de connexion au service Turnstile',
|
|
'error_codes' => ['internal-error']
|
|
];
|
|
}
|
|
|
|
// Vérification du code HTTP
|
|
if ($httpCode !== 200) {
|
|
error_log('Turnstile validation HTTP error: ' . $httpCode);
|
|
return [
|
|
'success' => false,
|
|
'error' => 'Erreur du service Turnstile (HTTP ' . $httpCode . ')',
|
|
'error_codes' => ['internal-error']
|
|
];
|
|
}
|
|
|
|
// Décodage de la réponse JSON
|
|
$result = json_decode($response, true);
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
error_log('Turnstile validation JSON decode error: ' . json_last_error_msg());
|
|
return [
|
|
'success' => false,
|
|
'error' => 'Erreur de parsing de la réponse Turnstile',
|
|
'error_codes' => ['internal-error']
|
|
];
|
|
}
|
|
|
|
// Validation du succès
|
|
if (!isset($result['success'])) {
|
|
error_log('Turnstile validation missing success field in response');
|
|
return [
|
|
'success' => false,
|
|
'error' => 'Réponse invalide du service Turnstile',
|
|
'error_codes' => ['internal-error']
|
|
];
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Valide un token avec gestion d'erreurs formatée pour l'affichage
|
|
*
|
|
* @param string $token Le token à valider
|
|
* @param string $remoteip L'adresse IP du client (optionnel)
|
|
* @return array Résultat formaté pour l'affichage
|
|
*/
|
|
public function validateForDisplay($token, $remoteip = null)
|
|
{
|
|
$result = $this->validate($token, $remoteip);
|
|
|
|
if ($result['success']) {
|
|
return [
|
|
'valid' => true,
|
|
'message' => '',
|
|
'data' => $result
|
|
];
|
|
}
|
|
|
|
// Mapping des erreurs pour l'affichage utilisateur
|
|
$errorMessages = [
|
|
'missing-input-secret' => 'Configuration Turnstile invalide.',
|
|
'invalid-input-secret' => 'Configuration Turnstile invalide.',
|
|
'missing-input-response' => 'Vérification de sécurité manquante. Veuillez cocher la case "Je ne suis pas un robot".',
|
|
'invalid-input-response' => 'Vérification de sécurité invalide. Veuillez réessayer.',
|
|
'bad-request' => 'Requête de vérification invalide.',
|
|
'timeout-or-duplicate' => 'Vérification de sécurité expirée. Veuillez réessayer.',
|
|
'internal-error' => 'Erreur temporaire du service de sécurité. Veuillez réessayer dans quelques instants.'
|
|
];
|
|
|
|
$errorCodes = isset($result['error-codes']) ? $result['error-codes'] : ['internal-error'];
|
|
$errorCode = reset($errorCodes); // Premier code d'erreur
|
|
|
|
$message = isset($errorMessages[$errorCode]) ? $errorMessages[$errorCode] : 'Erreur de vérification de sécurité inconnue.';
|
|
|
|
return [
|
|
'valid' => false,
|
|
'message' => $message,
|
|
'error_codes' => $errorCodes,
|
|
'data' => $result
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Vérifie si Turnstile est correctement configuré
|
|
*
|
|
* @return bool True si configuré, false sinon
|
|
*/
|
|
public static function isConfigured()
|
|
{
|
|
return defined('_CRED_TURNSTILE_SECRET_KEY_') &&
|
|
_CRED_TURNSTILE_SECRET_KEY_ !== '' &&
|
|
_CRED_TURNSTILE_SECRET_KEY_ !== 'YOUR_SECRET_KEY_HERE' &&
|
|
defined('_CRED_TURNSTILE_SITE_KEY_') &&
|
|
_CRED_TURNSTILE_SITE_KEY_ !== '' &&
|
|
_CRED_TURNSTILE_SITE_KEY_ !== 'YOUR_SITE_KEY_HERE';
|
|
}
|
|
}
|