Défense contre le credential stuffing avec Auth0 : stratégies avancées

Le credential stuffing utilise des bases de données de mots de passe volés pour compromettre des comptes. Auth0 offre plusieurs couches de défense — voici comment les combiner.

Par Auth0.Expert · 2025-04-15 · 10 min de lecture

Credential Stuffing Sécurité HIBP

Défense contre le credential stuffing avec Auth0

TL;DR

Les attaques de credential stuffing représentent 80-90% des tentatives de connexion malveillantes. Elles utilisent des listes de milliards de paires email/mot de passe provenant de fuites. Ce guide présente une défense en profondeur.

---

L'ampleur du problème

Avant d'explorer les solutions, il est important de comprendre l'ampleur et le coût des attaques de credential stuffing.

---

Couche 1 : Breached Password Detection (intégré Auth0)

Voici comment activer la détection de mots de passe compromis dans Auth0, afin de bloquer ou d'alerter en cas d'utilisation d'un mot de passe issu d'une fuite.

// Activer dans Dashboard → Security → Attack Protection
{
  "breached_password_detection": {
    "enabled": true,
    "shields": [
      "admin_notification",
      "user_notification",
      "block"
    ],
    "method": "standard"
  }
}

Auth0 compare les mots de passe à la base Have I Been Pwned (HIBP) de façon anonymisée via k-anonymity.

---

Couche 2 : Rate Limiting Adaptatif

Ce code bloque l'accès depuis des user-agents de bots connus lors de la connexion, pour limiter les attaques automatisées.

// Action Post Login : détecter le credential stuffing
exports.onExecutePostLogin = async (event, api) => {
  // Si trop de logins échoués depuis cette IP récemment
  // (Auth0 gère ça nativement, mais vous pouvez enrichir)
  const ip = event.request.ip;
  const userAgent = event.request.user_agent;

  // User-agent de bot connu
  const botUAPatterns = [
    /python-requests/i,
    /curl\//i,
    /Go-http-client/i,
    /axios\//i,
  ];

  if (botUAPatterns.some(p => p.test(userAgent))) {
    // Ajouter un délai / challenge
    api.access.deny('Vérification supplémentaire requise');
  }
};

Ce code enrichit la défense native d’Auth0 en ajoutant un filtrage spécifique de bots au niveau du login.

---

Couche 3 : Bot Detection avec Arkose Labs

Ici, on explique comment ajouter un challenge automatique contre les bots sur le formulaire Universal Login en intégrant Arkose Labs.

// Intégration Arkose Labs dans Universal Login
// Dashboard → Security → Bot Detection → Arkose Labs

// Configuration dans Universal Login template :
// Arkose Labs injecte automatiquement son challenge dans le formulaire
// Aucun code supplémentaire requis côté Auth0

Cette approche déploie la protection anti-bot sans développement côté back-end Auth0.

---

Couche 4 : Device Fingerprinting

Ce script Auth0 permet de détecter et de mémoriser l’empreinte des appareils des utilisateurs pour appliquer une vérification MFA en cas d’appareil inconnu.

// Action Post Login : fingerprinting avancé
exports.onExecutePostLogin = async (event, api) => {
  const fingerprint = {
    ua: event.request.user_agent?.substring(0, 100),
    ip: event.request.ip,
    country: event.request.geoip?.country_code,
    asn: event.request.geoip?.autonomous_system_number,
  };

  const knownFingerprints = event.user.app_metadata?.known_fingerprints || [];
  const fpHash = hashFingerprint(fingerprint);

  if (!knownFingerprints.includes(fpHash)) {
    if (knownFingerprints.length > 0) {
      // Nouvel appareil → MFA obligatoire
      api.multifactor.enable('any', { allowRememberBrowser: false });
    }

    // Stocker le nouveau fingerprint
    const updated = [...knownFingerprints.slice(-9), fpHash]; // Garder les 10 derniers
    api.user.setAppMetadata('known_fingerprints', updated);
  }
};

function hashFingerprint(fp) {
  // Implémentation simplifiée
  return btoa(JSON.stringify(fp)).substring(0, 32);
}

Ce code automatise la sécurisation par MFA dès qu’un nouvel appareil tente de se connecter sur un compte enregistré.

---

Couche 5 : Réponse aux incidents

Cette fonction backend illustre la réponse automatisée en cas de détection d'attaque, en activant des contre-mesures immédiates côté Auth0.

# Détection et réponse automatique au credential stuffing
# Via Log Streaming + votre backend

async def handle_credential_stuffing_alert(event: dict):
    """
    Déclenché quand on détecte:
    - > 1000 tentatives depuis des IPs distribuées en 1 heure
    - Pattern d'IPs : chacune utilisée 1-3 fois
    """
    # 1. Activer CAPTCHA obligatoire sur le tenant
    await auth0_mgmt.update_tenant_flags({
        "enable_public_signup_user_exists_error": False,
        # Cacher si l'email existe → empêche le harvesting
    })

    # 2. Alerter l'équipe sécurité
    await send_pagerduty_alert(
        severity="high",
        summary="Credential Stuffing détecté sur Auth0",
        details=event,
    )

    # 3. Activer MFA temporairement pour tous
    await create_auth0_rule_temp_mfa()

    # 4. Documenter pour l'audit
    await log_security_incident("credential_stuffing", event)

Ce bloc orchestre l’ensemble de la réponse à incident, depuis la détection à la documentation.

---

Indicateurs d'une attaque en cours

Voici comment surveiller et reconnaître les signaux typiques d'une attaque de credential stuffing lors du monitoring de vos logs Auth0.

| Signal | Valeur suspecte | Action | |--------|----------------|--------| | Ratio échecs/succès | > 90% | Alerte | | Tentatives/IP | 1-5 (distribué) | Alerte credential stuffing | | User-agents | Peu de variété | Bot probable | | Vitesse login | > 100/minute | Rate limiting insuffisant | | email_verified: false | Beaucoup | Comptes créés en masse |

---

Have I Been Pwned : vérification proactive

Ce code Python vérifie si un mot de passe figure dans les bases de données de fuites connues, en utilisant le service Have I Been Pwned avec k-anonymity pour garantir la confidentialité.

import hashlib
import httpx

async def check_hibp(password: str) -> bool:
    """Vérifie si un mot de passe est dans HIBP (k-anonymity)."""
    sha1 = hashlib.sha1(password.encode()).hexdigest().upper()
    prefix = sha1[:5]
    suffix = sha1[5:]

    resp = await httpx.AsyncClient().get(
        f"https://api.pwnedpasswords.com/range/{prefix}",
        headers={"Add-Padding": "true"},
    )

    # Chercher le suffix dans la réponse
    for line in resp.text.splitlines():
        hash_suffix, count = line.split(':')
        if hash_suffix == suffix:
            return True  # Mot de passe compromis

    return False

Cette fonction réalise une vérification proactive sans jamais exposer le mot de passe en clair.

---

> 🛡️ Réponse à incident credential stuffing : disponibles 24/7 pour analyser et stopper une attaque en cours contre votre système d'authentification.

📞 +33 1 23 45 67 89


Autres articles