Deux vecteurs d'attaque classiques ciblent systématiquement les interfaces d'administration web : les attaques CSRF (Cross-Site Request Forgery) qui exploitent la session authentifiée d'un administrateur, et les attaques par force brute sur le formulaire de connexion. QuietCMS implémente des contre-mesures pour les deux, sans dépendance externe.
Protection CSRF
Génération du token
À chaque démarrage de session, un token CSRF est généré avec random_bytes(32) encodé en hexadécimal (64 caractères) et stocké dans $_SESSION['csrf_token']. Ce token est lié à la session : deux onglets ouverts pour le même administrateur partagent le même token.
Intégration dans les formulaires
Chaque formulaire POST du back-office inclut un champ caché :
<input type="hidden" name="csrf_token"
value="<?= Security::getCsrfToken() ?>">
Vérification
À la réception du POST, la vérification utilise hash_equals() plutôt qu'une comparaison directe == :
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'] ?? '')) {
http_response_code(403);
die('Token CSRF invalide.');
}
hash_equals() effectue une comparaison en temps constant, ce qui élimine les attaques par timing qui exploiteraient la sortie précoce d'une comparaison ordinaire caractère par caractère.
Les formulaires publics gérés par FormManager disposent de leur propre token CSRF, indépendant du token d'administration, pour éviter toute interférence entre les deux contextes.
Rate limiting par IP
Périmètre
Le rate limiting est appliqué à deux points d'entrée :
- Formulaire de connexion : 5 tentatives échouées par IP sur une fenêtre glissante de 15 minutes.
- Formulaire de réinitialisation de mot de passe : 5 requêtes par IP par 15 minutes.
Stockage des IP : hachage partiel pour la conformité GDPR
Les adresses IP ne sont pas stockées en clair. Chaque IP est hachée avec SHA-256 et seuls les 16 premiers caractères hexadécimaux sont conservés — suffisamment pour identifier les tentatives répétées depuis la même source, insuffisant pour reconstruire l'IP originale :
$ipHash = substr(hash('sha256', $ipAddress), 0, 16);
Cette approche est conforme au RGPD : les données stockées ne sont pas des données personnelles directement identifiables.
Persistance sur disque
Les compteurs sont persistés dans .rate-limit.json, un fichier caché à la racine du dossier de données. Cette persistance est délibérée : elle empêche un attaquant de contourner le rate limiting en supprimant son cookie ou en ouvrant une session privée. Les entrées expirées (âge > 24 heures) sont purgées automatiquement à chaque lecture.
Logique de vérification
// Vérification avant traitement
if (Security::isRateLimited($ip, 'login')) {
die('Trop de tentatives. Réessayez dans 15 minutes.');
}
// Enregistrement d'un échec
if (!$loginSuccess) {
Security::recordRateLimitHit($ip, 'login');
}
// Nettoyage après succès
if ($loginSuccess) {
Security::clearRateLimit($ip, 'login');
}
Réinitialisation après succès
Lorsqu'une connexion réussit, le compteur de l'IP concernée est réinitialisé. Ceci évite de pénaliser un administrateur légitime qui aurait fait plusieurs fautes de frappe avant de saisir correctement son mot de passe.
Vue d'ensemble du modèle de défense
Ces deux mécanismes forment, avec l'URL admin randomisée et le chiffrement AES-256-GCM, un modèle de défense en profondeur adapté à un CMS léger : chaque couche compense les limites des autres, sans nécessiter de middleware externe ni de WAF dédié.
Articles similaires
URL admin randomisée : sécurité par l'obscurité
Comment QuietCMS génère une URL admin aléatoire à l'installation pour réduire la surface d'attaque.
Blocage IP, sous-réseau /24 et filtrage par pays
QuietCMS permet de bloquer des IPs individuelles, des sous-réseaux /24 et des pays entiers depuis le back-office. Implém…
Chiffrement AES-256-GCM des données sensibles
QuietCMS utilise AES-256-GCM pour chiffrer les mots de passe SMTP au repos. Détails techniques de l'implémentation.