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
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.
- 15 milliards de paires email/mot de passe disponibles sur le dark web
- Taux de réussite moyen : 0,1 à 2% (soit 15M à 300M de comptes compromis)
- Coût d'une attaque : moins de $100 avec des services de résolution CAPTCHA automatisée
- Détection difficile car chaque IP est utilisée une seule fois (botnet distribué)
---
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.
Autres articles
- Auth0 et OAuth 2.0 : architecture complète pour comprendre les flux d'autorisation
- OpenID Connect sur Auth0 : ID Token, UserInfo et discovery endpoint expliqués
- Configurer SAML 2.0 avec Auth0 : guide complet pour l'entreprise
- Migrer des Rules vers les Auth0 Actions : guide pas à pas
- Architecture multi-tenant avec Auth0 : organisations, isolation et bonnes pratiques
- Validation des JWT Auth0 : RS256 vs HS256, JWKS et pièges communs
- Concevoir son architecture de tenants Auth0 : dev, staging, prod
- Configurer un domaine personnalisé Auth0 : login.votredomaine.com étape par étape