Guide de Sécurité¶
Tenxyte fournit plusieurs couches de sécurité prêtes à l'emploi.
Table des Matières¶
- Limitation de Débit (Rate Limiting)
- Verrouillage de Compte
- Authentification à Deux Facteurs (2FA / TOTP)
- Sécurité des Jetons JWT
- Limites de Sessions et d'Appareils
- Sécurité des Mots de Passe
- En-têtes de Sécurité
- CORS
- Journaux d'Audit (Audit Logging)
- Vérification OTP
- Checklist pour la Production
Limitation de Débit (Rate Limiting)¶
Classes de Limitation Intégrées¶
Tenxyte est livré avec des classes de limitation préconfigurées pour les points de terminaison sensibles :
| Classe | Débit par Défaut | Appliqué à |
|---|---|---|
LoginThrottle |
5/min | Points de terminaison de connexion |
LoginHourlyThrottle |
20/heure | Points de terminaison de connexion |
RegisterThrottle |
3/heure | Point de terminaison d'inscription |
RegisterDailyThrottle |
10/jour | Point de terminaison d'inscription |
RefreshTokenThrottle |
30/min | Rafraîchissement de jeton |
ProgressiveLoginThrottle |
Progressif | Points de terminaison de connexion (augmente le délai après chaque échec) |
OTPRequestThrottle |
5/heure | Demande d'OTP |
OTPVerifyThrottle |
5/min | Vérification d'OTP |
PasswordResetThrottle |
3/heure | Demande de réinitialisation de mot de passe |
PasswordResetDailyThrottle |
10/jour | Demande de réinitialisation de mot de passe |
MagicLinkRequestThrottle |
3/heure | Demande de lien magique |
MagicLinkVerifyThrottle |
10/min | Vérification de lien magique |
Limitation Personnalisée Basée sur l'URL¶
Appliquez des limites de débit à n'importe quelle URL sans écrire de classe personnalisée :
# settings.py
TENXYTE_SIMPLE_THROTTLE_RULES = {
'/api/v1/products/': '100/hour',
'/api/v1/search/': '30/min',
'/api/v1/upload/': '5/hour',
'/api/v1/health/$': '1000/min', # $ = correspondance exacte
}
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'tenxyte.throttles.SimpleThrottleRule',
],
}
Désactiver la Limitation dans les Tests¶
# Dans vos helpers de test
from unittest.mock import patch
with patch('rest_framework.throttling.SimpleRateThrottle.allow_request', return_value=True):
response = client.post('/api/v1/auth/login/email/', data)
Verrouillage de Compte¶
Après TENXYTE_MAX_LOGIN_ATTEMPTS (par défaut : 5) tentatives de connexion échouées dans un intervalle de TENXYTE_RATE_LIMIT_WINDOW_MINUTES (par défaut : 15 min), le compte est verrouillé pendant TENXYTE_LOCKOUT_DURATION_MINUTES (par défaut : 30 min).
# settings.py
TENXYTE_ACCOUNT_LOCKOUT_ENABLED = True
TENXYTE_MAX_LOGIN_ATTEMPTS = 5
TENXYTE_LOCKOUT_DURATION_MINUTES = 30
TENXYTE_RATE_LIMIT_WINDOW_MINUTES = 15
Les comptes verrouillés renvoient un 401 avec le code : 'ACCOUNT_LOCKED'.
Verrouillage Exponentiel¶
Lorsque TENXYTE_LOCKOUT_ESCALATION_ENABLED = True (par défaut), chaque verrouillage consécutif double la durée, plafonnée à TENXYTE_LOCKOUT_MAX_DURATION_MINUTES (par défaut : 1440 = 24h) :
| Verrouillage # | Durée (base=30min) |
|---|---|
| 1er | 30 min |
| 2ème | 60 min |
| 3ème | 120 min |
| 4ème | 240 min |
| 5ème+ | 1440 min (plafond 24h) |
Le compteur se réinitialise après une connexion réussie. Formule : min(base × 2^(n-1), durée_max).
Déverrouillage par l'administrateur via l'API :
Authentification à Deux Facteurs (2FA / TOTP)¶
Flux de Configuration¶
- L'utilisateur appelle
POST /2fa/setup/→ reçoit un code QR + codes de secours. - L'utilisateur scanne le code QR avec Google Authenticator / Authy.
- L'utilisateur appelle
POST /2fa/confirm/avec le premier code TOTP → la 2FA est activée.
Connexion avec 2FA¶
POST /api/v1/auth/login/email/
{
"email": "user@example.com",
"password": "SecurePass123!",
"totp_code": "123456"
}
Si le totp_code est manquant alors que la 2FA est activée, la réponse est :
Codes de Secours¶
Générés lors de la configuration (TENXYTE_BACKUP_CODES_COUNT, par défaut : 10).
Chaque code est à usage unique. Régénérez-les avec POST /2fa/backup-codes/.
Configuration¶
TENXYTE_TOTP_ISSUER = 'MonApp' # Nom affiché dans l'application d'authentification
TENXYTE_TOTP_VALID_WINDOW = 1 # Accepter ±1 période (tolérance de 30s)
TENXYTE_BACKUP_CODES_COUNT = 10
Sécurité des Jetons JWT¶
Algorithme¶
Tenxyte utilise HS256 (HMAC-SHA256) par défaut. En production, passez à un algorithme asymétrique (RS256, EdDSA) pour éviter de partager le secret de signature entre les services :
# settings.py
TENXYTE_JWT_ALGORITHM = 'RS256'
TENXYTE_JWT_PRIVATE_KEY = open('/secrets/jwt_private.pem').read()
TENXYTE_JWT_PUBLIC_KEY = open('/secrets/jwt_public.pem').read()
Note : Lorsque
HS256est détecté, Tenxyte émet unSecurityWarningau démarrage du service. Utilisez le préréglageproductionouenterprisepour passer automatiquement à RS256.
Mise sur Liste Noire (Blacklisting) des Jetons d'Accès¶
Lorsque TENXYTE_TOKEN_BLACKLIST_ENABLED = True (par défaut), les jetons d'accès sont mis sur liste noire lors de la déconnexion. Cela empêche la réutilisation du jeton même avant son expiration.
Rotation des Jetons de Rafraîchissement¶
Lorsque TENXYTE_REFRESH_TOKEN_ROTATION = True (par défaut), chaque appel à /refresh/ délivre un nouveau jeton de rafraîchissement et invalide l'ancien. Cela limite les dégâts en cas de vol d'un jeton de rafraîchissement.
Jetons d'Accès à Courte Durée de Vie¶
Les jetons d'accès expirent après 15 minutes par défaut. Cela limite la fenêtre d'exposition en cas de compromission d'un jeton. Appuyez-vous sur les jetons de rafraîchissement pour la persistance de session :
# Défauts — déjà sécurisés
TENXYTE_JWT_ACCESS_TOKEN_LIFETIME = 900 # 15 minutes
TENXYTE_JWT_REFRESH_TOKEN_LIFETIME = 604800 # 7 jours
RGPD / Minimisation des Données¶
Évitez d'inclure des DCP (adresse e-mail, adresse IP, numéro de téléphone, etc.) dans les payloads JWT. Les jetons JWT sont encodés en base64 et lisibles par quiconque les intercepte. Tenxyte émet un SecurityWarning si des clés sensibles sont détectées dans extra_claims :
# ❌ Ne jamais faire ceci
jwt_service.generate_access_token(user_id, app_id, extra_claims={"email": user.email})
# ✅ Rechercher par user_id à la place
user = User.objects.get(pk=decoded_token.user_id)
Clés qui déclenchent l'avertissement : email, password, phone, phone_number, ip, ip_address, address, ssn, credit_card, dob, date_of_birth.
Claims Obligatoires¶
Tous les jetons émis incluent exp, iat et jti. Lorsque JWT_ISSUER ou JWT_AUDIENCE sont configurés, iss et aud sont également requis — les jetons les manquant sont rejetés au niveau du décodage PyJWT.
Rotation des Clés¶
Effectuez une rotation des clés de signature JWT sans invalider les jetons actifs. Définissez la clé précédente pour que les jetons existants puissent encore être vérifiés pendant la transition :
# 1. Générer une nouvelle clé
# 2. Déplacer la clé actuelle vers PREVIOUS
TENXYTE_JWT_PREVIOUS_SECRET_KEY = '<ancienne-cle>' # HS256
# ou pour RS256 :
TENXYTE_JWT_PREVIOUS_PUBLIC_KEY = open('/secrets/old_jwt_public.pem').read()
# 3. Définir la nouvelle clé
TENXYTE_JWT_SECRET_KEY = '<nouvelle-cle>'
Les nouveaux jetons sont signés avec la clé actuelle. Au décodage, si la clé primaire échoue avec InvalidSignatureError, Tenxyte réessaie avec la clé précédente. Retirez PREVIOUS_* une fois que tous les anciens jetons ont expiré.
Transport par Cookie des Jetons de Rafraîchissement¶
Mode opt-in pour transporter les jetons de rafraîchissement dans des cookies HttpOnly; Secure; SameSite au lieu du corps JSON, empêchant le vol de jetons par XSS :
TENXYTE_REFRESH_TOKEN_COOKIE_ENABLED = True # Défaut : False
TENXYTE_REFRESH_TOKEN_COOKIE_NAME = 'tenxyte_refresh'
TENXYTE_REFRESH_TOKEN_COOKIE_SAMESITE = 'Strict'
TENXYTE_REFRESH_TOKEN_COOKIE_PATH = '/api/v1/auth/'
Lorsqu'activé :
- Réponses de connexion/rafraîchissement : refresh_token est retiré du corps JSON et défini dans un en-tête Set-Cookie.
- Requêtes de rafraîchissement : Le serveur lit le jeton de rafraîchissement depuis le cookie si le corps est vide.
- Déconnexion : Le cookie est effacé avec max-age=0.
Important : Ce mode est opt-in et désactivé par défaut. Les clients doivent gérer l'absence de
refresh_tokendans les réponses JSON.
Limites de Sessions et d'Appareils¶
Limites de Sessions¶
Limitez le nombre de sessions simultanées qu'un utilisateur peut avoir en rejetant ou en remplaçant des connexions. Par défaut, Tenxyte limite les utilisateurs à 1 session :
TENXYTE_SESSION_LIMIT_ENABLED = True
TENXYTE_DEFAULT_MAX_SESSIONS = 1 # Remplacé par le préréglage standard à 3
TENXYTE_DEFAULT_SESSION_LIMIT_ACTION = 'revoke_oldest' # ou 'deny'
Actions :
- 'revoke_oldest' — révoque la session la plus ancienne pour faire de la place.
- 'deny' — rejette la nouvelle tentative de connexion.
Remplacement par utilisateur : définissez user.max_sessions = 5 pour outrepasser la valeur par défaut pour cet utilisateur.
Limites d'Appareils¶
Limitez le nombre d'appareils uniques qu'un utilisateur peut utiliser en suivant les origines structurées des appareils. Par défaut, Tenxyte limite les utilisateurs à 1 appareil :
TENXYTE_DEVICE_LIMIT_ENABLED = True
TENXYTE_DEFAULT_MAX_DEVICES = 1 # Remplacé par le préréglage standard à 5, haute sécurité à 2
TENXYTE_DEVICE_LIMIT_ACTION = 'deny' # ou 'revoke_oldest'
L'identification de l'appareil utilise le champ device_info envoyé par le client (chaîne d'empreinte structurée). Tenxyte utilise une correspondance intelligente — les différences de version mineures (ex: Chrome 122 vs 123) sont traitées comme le même appareil.
Format d'information d'appareil (v1) :
Construction à partir du User-Agent comme solution de repli :
from tenxyte.device_info import build_device_info_from_user_agent
device_info = build_device_info_from_user_agent(request.META.get('HTTP_USER_AGENT', ''))
Sécurité des Mots de Passe¶
Politique de Mot de Passe¶
TENXYTE_PASSWORD_MIN_LENGTH = 8
TENXYTE_PASSWORD_MAX_LENGTH = 128
TENXYTE_PASSWORD_REQUIRE_UPPERCASE = True
TENXYTE_PASSWORD_REQUIRE_LOWERCASE = True
TENXYTE_PASSWORD_REQUIRE_DIGIT = True
TENXYTE_PASSWORD_REQUIRE_SPECIAL = True
Historique des Mots de Passe¶
Empêche les utilisateurs de réutiliser leurs mots de passe récents :
TENXYTE_PASSWORD_HISTORY_ENABLED = True
TENXYTE_PASSWORD_HISTORY_COUNT = 5 # Vérifier par rapport aux 5 derniers mots de passe
Vérifier la Force d'un Mot de Passe¶
Conformité NIST SP 800-63B¶
Les comptes sans 2FA activée peuvent être soumis à une longueur minimale de mot de passe plus élevée, conformément aux recommandations NIST SP 800-63B :
Lorsque défini à 15, les utilisateurs sans 2FA doivent utiliser des mots de passe d'au moins 15 caractères. Les utilisateurs avec 2FA active continuent d'utiliser le PASSWORD_MIN_LENGTH standard (défaut : 8).
En-têtes de Sécurité¶
Ajoutez des en-têtes de sécurité à toutes les réponses. Par défaut, Tenxyte fournit un ensemble d'en-têtes très restrictif, mais ils sont désactivés (False) par défaut à moins que vous n'utilisiez un préréglage de sécurité ou que vous ne les activiez manuellement :
TENXYTE_SECURITY_HEADERS_ENABLED = False # Définir sur True pour activer
TENXYTE_SECURITY_HEADERS = {
'X-Content-Type-Options': 'nosniff',
'X-XSS-Protection': '1; mode=block',
'X-Frame-Options': 'DENY',
'Referrer-Policy': 'strict-origin-when-cross-origin',
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
'Content-Security-Policy': "default-src 'none'; frame-ancestors 'none'",
'Cross-Origin-Resource-Policy': 'same-origin',
'Cross-Origin-Opener-Policy': 'same-origin',
'Permissions-Policy': 'camera=(), microphone=(), geolocation=()',
}
Ajoutez le middleware :
CORS¶
TENXYTE_CORS_ENABLED = True
TENXYTE_CORS_ALLOWED_ORIGINS = [
'https://votreapp.com',
'http://localhost:3000',
]
TENXYTE_CORS_ALLOW_CREDENTIALS = True
Ajoutez le middleware :
Journaux d'Audit (Audit Logging)¶
Tous les événements relatifs à la sécurité sont automatiquement enregistrés dans le modèle AuditLog :
| Événement | Déclencheur |
|---|---|
login |
Connexion réussie |
login_failed |
Tentative de connexion échouée |
logout |
Déconnexion de l'utilisateur |
logout_all |
Déconnexion de tous les appareils |
token_refresh |
Jeton d'accès rafraîchi |
password_change |
Mot de passe changé |
password_reset_request |
Demande de réinitialisation de mot de passe |
password_reset_complete |
Réinitialisation de mot de passe terminée |
2fa_enabled |
2FA activée |
2fa_disabled |
2FA désactivée |
2fa_backup_used |
Code de secours 2FA utilisé |
account_created |
Compte créé |
account_locked |
Compte verrouillé après des échecs |
account_unlocked |
Compte déverrouillé |
email_verified |
E-mail vérifié |
phone_verified |
Téléphone vérifié |
role_assigned |
Rôle attribué |
role_removed |
Rôle retiré |
permission_changed |
Permission changée |
app_created |
Application créée |
app_credentials_regenerated |
Identifiants d'application régénérés |
account_deleted |
Compte supprimé |
suspicious_activity |
Activité suspecte détectée |
session_limit_exceeded |
Limite de sessions atteinte |
device_limit_exceeded |
Limite d'appareils atteinte |
new_device_detected |
Connexion depuis un appareil non reconnu |
agent_action |
Action d'agent exécutée |
Consulter les journaux d'audit :
Vérification OTP¶
La vérification de l'e-mail et du téléphone utilise des codes OTP limités dans le temps :
TENXYTE_OTP_LENGTH = 6
TENXYTE_OTP_EMAIL_VALIDITY = 15 # minutes
TENXYTE_OTP_PHONE_VALIDITY = 10 # minutes
TENXYTE_OTP_MAX_ATTEMPTS = 5 # avant invalidation
Checklist pour la Production¶
- [ ] Définissez
TENXYTE_JWT_SECRET_KEYsur un secret fort et unique (différent deSECRET_KEY). - [ ] Passez à
TENXYTE_JWT_ALGORITHM = 'RS256'et configurez des clés RSA. - [ ] Définissez
TENXYTE_JWT_ACCESS_TOKEN_LIFETIMEsur ≤ 900 secondes (15 min) — c'est désormais la valeur par défaut. - [ ] Activez
TENXYTE_REFRESH_TOKEN_ROTATION = True. - [ ] Activez
TENXYTE_TOKEN_BLACKLIST_ENABLED = True. - [ ] Définissez
TENXYTE_JWT_AUDIENCEavec l'identifiant de votre application. - [ ] Activez
TENXYTE_SECURITY_HEADERS_ENABLED = True. - [ ] Configurez
TENXYTE_CORS_ALLOWED_ORIGINS(ne jamais utiliserALLOW_ALL_ORIGINSen production). - [ ] Définissez
TENXYTE_MAX_LOGIN_ATTEMPTSsur une valeur raisonnable (5–10). - [ ] Activez
TENXYTE_PASSWORD_HISTORY_ENABLED = True. - [ ] Utilisez HTTPS en production (requis pour
Strict-Transport-Security). - [ ] Effectuez une rotation régulière des secrets d'
Application. - [ ] Assurez-vous que
extra_claimsne contient pas de DCP (email, IP, téléphone…). - [ ] Envisagez d'activer le transport par cookie pour les jetons de rafraîchissement pour les applications web.
- [ ] Envisagez d'activer la longueur NIST pour les comptes sans MFA (
PASSWORD_MIN_LENGTH_NO_MFA = 15).
Sécurité OAuth / Connexion Sociale¶
PKCE (Proof Key for Code Exchange)¶
Tous les fournisseurs OAuth supportent PKCE (RFC 7636). Les clients doivent inclure code_verifier dans la requête d'échange de code :
POST /api/v1/auth/social/google/
{
"code": "<code-d-autorisation>",
"redirect_uri": "https://votre-app.com/auth/callback",
"code_verifier": "<vérificateur-PKCE>"
}
Le code_verifier est transmis au endpoint de jeton du fournisseur. Cela empêche les attaques d'interception de code d'autorisation.
Liste Blanche des URI de Redirection¶
Configurez les URI de redirection autorisées par modèle Application. Lorsque la liste n'est pas vide, tout redirect_uri non présent retourne 400 INVALID_REDIRECT_URI :
# Via l'admin Django ou l'API
app.redirect_uris = [
"https://votre-app.com/auth/callback",
"https://staging.votre-app.com/auth/callback",
]
Scopes OAuth Configurables¶
Surchargez les scopes par défaut par fournisseur :
TENXYTE_SOCIAL_GOOGLE_SCOPES = 'openid email profile'
TENXYTE_SOCIAL_GITHUB_SCOPES = 'read:user user:email'
TENXYTE_SOCIAL_MICROSOFT_SCOPES = 'openid email profile'
TENXYTE_SOCIAL_FACEBOOK_SCOPES = 'email,public_profile'
Pour la liste complète des paramètres avec leurs valeurs par défaut, voir la Référence des Paramètres.