CSRF (Cross-Site Request Forgery) en PHP

Publié le 12/01/2026 par Frédéric dans la catégorie "Php"

Le CSRF (Cross-Site Request Forgery) est une attaque qui force un utilisateur authentifié à exécuter des actions non désirées sur une application web, sans son consentement.

  • Mécanisme : Un site malveillant (ou un email piégé) envoie une requête HTTP à l’insu de la victime vers un site vulnérable (ex: bank.com/transfer?to=hacker&amount=1000), en exploitant :
    • Les cookies de session déjà actifs (l’utilisateur est connecté).
    • L’absence de vérification de l’origine de la requête.
  • Impact :
    • Modification de données (ex: changement de mot de passe, transfert d’argent).
    • Actions administratives (suppression de compte, envoi de messages).
  • Exemple concret : Une victime connectée à facebook.com clique sur un lien malveillant qui envoie une requête POST pour publier un message à son insu ou ajouter un ami frauduleux.

⚠️ Pourquoi c’est dangereux ? Contrairement au XSS (qui vole des données), le CSRF exploite la confiance du serveur envers l’utilisateur authentifié.

Prompt pour l'IA

Analyse ce code PHP pour détecter des vulnérabilités Cross-Site Request Forgery (CSRF). Vérifie les points suivants :

1. Absence de tokens CSRF

  • Critères :
    • Les formulaires (GET/POST) ou requêtes sensibles (modification de données, suppression, transactions) n’utilisent pas de token CSRF.
    • Recherche de motifs dangereux :
      // ❌ Vulnérable : Aucun token CSRF
      <form action="transfer.php" method="POST">
      <input type="hidden" name="amount" value="1000">
      <input type="submit" value="Envoyer">
      </form>
    • Vérifie l’absence de :
    • csrf_token dans les formulaires.
    • Fonctions de génération de token (bin2hex(random_bytes(32)), hash_hmac, ou librairies comme Symfony/CSRF).

2. Méthodes HTTP non protégées

  • Requêtes GET pour des actions sensibles :
    • Exemple de code dangereux :
      // ❌ GET pour une action critique (doit être POST + token)
      if ($_GET['action'] === 'delete_account') {
      deleteUserAccount($_GET['user_id']);
      }
  • En-têtes Referer/Origin non vérifiés :
    • Le code ne vérifie pas si la requête vient bien du même domaine ($_SERVER['HTTP_REFERER'] ou $_SERVER['HTTP_ORIGIN']).

3. Mauvaise gestion des cookies

  • Cookies sans attributs SameSite :
    • Recherche de setcookie() sans :
      setcookie("session", $token, [
      'samesite' => 'Strict', // ou 'Lax'
      'secure' => true,       // HTTPS obligatoire
      'httponly' => true      // Empêche l'accès via JavaScript
      ]);
  • Sessions vulnérables :
    • Utilisation de session_start() sans régénération de l’ID de session après login (session_regenerate_id(true)).

4. Fausses protections

  • Tokens CSRF statiques ou prévisibles :
    • Exemple de code inefficace :
      // ❌ Token prévisible (basé sur l'IP ou un timestamp)
      $csrf_token = md5($_SERVER['REMOTE_ADDR'] . time());
    • Les tokens doivent être uniques par session et par formulaire, stockés côté serveur (ex: en $_SESSION).

Question bonus pour l’IA : "Propose des correctifs pour chaque vulnérabilité identifiée, en utilisant les bonnes pratiques PHP modernes (ex: librairies comme paragonie/csrf-magic ou les composants Symfony)."

Explication détaillée

🔍 Comment fonctionne une attaque CSRF ?

Scénario type :

  1. La victime est connectée à vulnerable-site.com (son cookie de session est actif).
  2. Elle visite un site malveillant (ou clique sur un lien dans un email) contenant :
    <!-- Code HTML malveillant (ex: dans une iframe invisible) -->
    <img src="https://vulnerable-site.com/change-email?new_email=hacker@evil.com" width="0" height="0">
  3. Le navigateur envoie automatiquement la requête avec le cookie de session de la victime.
  4. Le serveur exécute l’action sans vérifier l’origine de la requête.

Pourquoi ça marche ?

  • Les cookies sont envoyés automatiquement par le navigateur pour les requêtes vers le même domaine.
  • Le serveur ne fait pas la différence entre une requête légitime et une requête forcée.

🛡️ Solutions pour se protéger en PHP

1. Utiliser des tokens CSRF

Principe : Ajouter un token unique et imprévisible à chaque formulaire, et le vérifier côté serveur.

Exemple avec PHP natif :

// Génération du token (à faire une fois par session)
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// Dans le formulaire HTML
<form action="transfer.php" method="POST">
  <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
  <input type="text" name="amount">
  <input type="submit" value="Envoyer">
</form>

// Vérification côté serveur
if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
    die("CSRF token invalid!");
}

Avec une librairie (recommandé) :

// Utilisation de paragonie/csrf-magic (composer require paragonie/csrf-magic)
require 'vendor/autoload.php';
$csrf = new \ParagonIE\AntiCSRF\AntiCSRF;

// Dans le formulaire
<input type="hidden" name="csrf_token" value="<?php echo $csrf->insertToken(); ?>">

// Vérification
try {
    $csrf->validateRequest();
} catch (\ParagonIE\AntiCSRF\AntiCSRFException $e) {
    die("CSRF validation failed!");
}

2. Vérifier l’en-tête Referer ou Origin

Limite : Peut être contourné si l’attaquant utilise un sous-domaine ou un protocole différent (HTTP vs HTTPS).

$referer = $_SERVER['HTTP_REFERER'] ?? '';
$allowed_domain = 'https://votre-site.com';

if (strpos($referer, $allowed_domain) !== 0) {
    die("Requête non autorisée (mauvais Referer).");
}

3. Configurer les cookies avec SameSite

Solution la plus simple (mais nécessite HTTPS) :

// Dans php.ini ou via .htaccess
session.cookie_samesite = "Strict" // ou "Lax"

// Ou en PHP
ini_set('session.cookie_samesite', 'Strict');
session_start();
  • Strict : Le cookie n’est envoyé que pour les requêtes vers le même site (idéal pour les zones sensibles).
  • Lax : Autorise les liens externes (ex: depuis un email), mais bloque les requêtes POST cross-site.

4. Privilégier les requêtes POST pour les actions sensibles

Règle d’or :

  • GET = Récupération de données (idempotent).
  • POST = Modification de données (doit être protégé par CSRF).

Exemple de middleware PHP pour forcer POST :

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    die("Méthode non autorisée.");
}

5. Utiliser des frameworks modernes

Les frameworks PHP populaires intègrent déjà des protections CSRF :

  • Laravel : Token CSRF automatique via @csrf dans les formulaires.
  • Symfony : Composant Security/CSRF avec csrf_token().
  • WordPress : Fonction wp_nonce_field().

Exemple avec Laravel :

// Dans un formulaire Blade
<form method="POST" action="/transfer">
    @csrf <!-- Génère un token CSRF -->
    <input type="text" name="amount">
</form>

🎯 Bonnes pratiques supplémentaires

Pratique Exemple de code Pourquoi ?
Régénérer l’ID de session session_regenerate_id(true); Empêche la fixation de session.
Désactiver GET pour les actions if ($_SERVER['REQUEST_METHOD'] !== 'POST') { die(); } Évite les attaques via <img src="...">.
Utiliser des CAPTCHA Librairie comme google/recaptcha Ajoute une couche de validation humaine.
Logger les tentatives CSRF error_log("CSRF failed for IP: " . $_SERVER['REMOTE_ADDR']); Détection des attaques.

🔧 Outils pour tester les vulnérabilités CSRF

  1. Extensions navigateur :
    • Cookie-Editor : Pour modifier les cookies et tester les protections SameSite.
    • Burp Suite : Intercepter/modifier les requêtes pour simuler une attaque.
  2. Scripts Python :
    # Exemple avec requests pour tester une vulnérabilité CSRF
    import requests
    url = "https://vulnerable-site.com/change-email"
    cookies = {"sessionid": "volé_ou_deviné"}  # Cookie de la victime
    data = {"email": "hacker@evil.com"}
    response = requests.post(url, cookies=cookies, data=data)
    print(response.text)  # Vérifie si l'action est exécutée
  3. Outils automatisés :
    • OWASP ZAP : Scanner de vulnérabilités CSRF.
    • Nikto : nikto -host exemple.com -C all (recherche de formulaires non protégés).

📌 Résumé des actions clés pour les développeurs PHP

Étape Action
1. Identifier les zones à risque Tous les formulaires/modifications de données (POST/GET/PUT/DELETE).
2. Ajouter des tokens CSRF Utiliser bin2hex(random_bytes(32)) ou une librairie dédiée.
3. Configurer les cookies SameSite=Strict, Secure, HttpOnly.
4. Valider les en-têtes Vérifier Referer ou Origin (avec prudence).
5. Tester Utiliser Burp Suite ou OWASP ZAP pour simuler des attaques.

🚨 Exemple réel d’exploitation CSRF (cas historique)

Attaque sur YouTube (2008) :

  • Un pirate a exploité une faille CSRF pour forcer les utilisateurs à s’abonner à des chaînes malveillantes.
  • Méthode :
    • Un lien piégé (<img src="https://youtube.com/subscribe?channel=HACKER">) était partagé sur des forums.
    • Les victimes connectées à YouTube s’abonnaient automatiquement en visitant la page.
  • Correctif : YouTube a ajouté des tokens CSRF et limité les actions sensibles aux requêtes POST.

📚 Ressources pour approfondir


💡 Message final :

"Le CSRF est une attaque sournoise car elle exploite un comportement normal du web : la confiance dans les cookies. La bonne nouvelle ? Avec des tokens CSRF et une configuration stricte des cookies, vous pouvez éliminer 99% des risques. Ne négligez pas cette protection – elle prend 5 minutes à implémenter et peut sauver votre application."


❓ Questions fréquentes

  1. "Les tokens CSRF suffisent-ils ?" → Oui, si ils sont uniques par session/formulaire et vérifiés côté serveur. Mais combinez-les avec SameSite pour une protection optimale.

  2. "Pourquoi ne pas bloquer toutes les requêtes cross-origin ?" → Certains sites légitimes ont besoin de requêtes cross-origin (ex: API publiques). Les tokens CSRF permettent une protection ciblée.

  3. "Mon site est en HTTP, puis-je utiliser SameSite ?" → Non, SameSite nécessite HTTPS. Passez en HTTPS avant de l’implémenter.