
Une application Django réutilisable simple et super rapide qui bloque les tentatives de connexion par force brute.
.. image:: https://jazzband.co/static/img/badge.svg :target: https://jazzband.co/ :alt: Jazzband
.. image:: https://img.shields.io/pypi/pyversions/django-defender.svg :alt: Versions Python prises en charge :target: https://pypi.org/project/django-defender/
.. image:: https://img.shields.io/pypi/djversions/django-defender.svg :target: https://pypi.org/project/django-defender/ :alt: Versions Django prises en charge
.. image:: https://github.com/jazzband/django-defender/workflows/Test/badge.svg :target: https://github.com/jazzband/django-defender/actions :alt: GitHub Actions
.. image:: https://codecov.io/gh/jazzband/django-defender/branch/master/graph/badge.svg :target: https://codecov.io/gh/jazzband/django-defender :alt: Couverture
.. image:: https://readthedocs.org/projects/django-defender/badge/?version=latest :alt: Statut de la documentation :target: https://django-defender.readthedocs.io/en/latest/?badge=latest
Une simple application Django réutilisable qui empêche les tentatives de connexion par force brute. L'objectif est de la rendre aussi rapide que possible, afin de ne pas ralentir les tentatives de connexion.
Nous utiliserons un cache pour éviter d'interroger la base de données à chaque tentative de connexion. La première version sera basée sur Redis, mais l'objectif est de rendre cela configurable afin que les utilisateurs puissent utiliser le backend qui correspond le mieux à leurs besoins.
Si vous utilisez defender sur votre site, soumettez une PR pour l'ajouter à la liste.
La documentation est disponible sur Read the Docs :
https://django-defender.readthedocs.io
Enregistrer toutes les tentatives de connexion dans la base de données
Support des proxys inverses avec différents en-têtes pour les adresses IP
Limitation de débit basée sur
Utilisation de Redis pour la liste noire
Configuration
Serveur Redis
Durée du blocage
Nombre de tentatives incorrectes avant blocage
95 % de couverture de code
Documentation complète
Capacité de stocker les tentatives de connexion dans la base de données
Commande de gestion pour nettoyer la table des tentatives de connexion
Pages d'administration
Peut être facilement adapté à une méthode d'authentification personnalisée.
Des signaux sont envoyés lors du blocage d'un nom d'utilisateur ou d'une IP
Pages d'administration
.. image:: https://cloud.githubusercontent.com/assets/261601/5950540/8895b570-a729-11e4-9dc3-6b00e46c8043.png :target: https://cloud.githubusercontent.com/assets/261601/5950540/8895b570-a729-11e4-9dc3-6b00e46c8043.png :alt: alt tag
.. image:: https://cloud.githubusercontent.com/assets/261601/5950541/88a35194-a729-11e4-981b-3a55b44ef9d5.png :target: https://cloud.githubusercontent.com/assets/261601/5950541/88a35194-a729-11e4-981b-3a55b44ef9d5.png :alt: alt tag
Téléchargez le code et exécutez l'installation de l'une des manières suivantes selon la méthode.
Pour installer la version prête pour la production depuis PyPI :
.. code-block:: bash
pip install django-defender
Pour installer la version de développement à partir du code source après téléchargement :
.. code-block:: bash
python setup.py install
Pour installer la version de développement de la branche master depuis le dépôt GitHub :
.. code-block:: bash
pip install -e git+http://github.com/kencochran django-defender.git#egg=django_defender-dev
Tout d'abord, vous devez ajouter ce projet à votre liste INSTALLED_APPS dans settings.py
.. code-block:: python
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', # ... 'defender', # ... ]
Ensuite, installez le middleware FailedLoginMiddleware
.. code-block:: python
MIDDLEWARE_CLASSES = [ 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'defender.middleware.FailedLoginMiddleware', ]
Si vous souhaitez gérer les utilisateurs bloqués via l'administration Django, ajoutez ce qui suit à votre urls.py
.. code-block:: python
urlpatterns = [ path('admin/defender/', include('defender.urls')), # defender admin path('admin/', admin.site.urls), # normal admin # your own patterns follow... ]
Migrations
Vous devrez créer les tables nécessaires dans votre base de données.
.. code-block:: bash
python manage.py migrate defender
Commandes de gestion
cleanup_django_defender
Si votre site web a beaucoup de trafic, la table AccessAttempts se remplira rapidement. Si vous n'avez pas besoin de conserver les données à des fins d'audit, une commande de gestion vous aide à la nettoyer.
Elle utilise le paramètre DEFENDER_ACCESS_ATTEMPT_EXPIRATION pour déterminer les enregistrements à supprimer. Par défaut, si non spécifié, la durée est de 24 heures.
.. code-block:: bash
$ python manage.py cleanup_django_defender
Vous pouvez configurer cette commande comme une tâche cron quotidienne ou hebdomadaire pour réduire la taille de la table.
.. code-block:: bash
24 0 * * * /usr/bin/python manage.py cleanup_django_defender >> /var/log/django_defender_cleanup.log
Performances
L'objectif de defender est d'être aussi rapide que possible afin de ne pas ralentir le processus de connexion. Pour vérifier que nos objectifs sont atteints, nous avons besoin d'un moyen de tester l'application. La meilleure façon est de comparer la rapidité d'une connexion Django normale avec defender et django-axes.
La connexion Django normale est notre base de référence, et nous nous attendons à ce qu'elle soit la plus rapide des trois méthodes, car il n'y a pas de vérifications supplémentaires.
La connexion avec defender sera probablement plus lente que la connexion Django, et nous espérons qu'elle sera plus rapide que la connexion avec django-axes. L'objectif est de minimiser la différence entre la connexion brute normale et defender.
La vitesse de connexion avec django-axes sera probablement la plus lente des trois car elle effectue plus de vérifications et beaucoup de requêtes en base de données.
La meilleure façon de déterminer la vitesse d'une connexion est d'effectuer un test de charge sur une application avec chaque configuration, et de comparer les temps de connexion pour chaque type.
Tests de charge
Pour couvrir tous les types de connexions, notre test de charge doit inclure plusieurs scénarios.
#. Tout succès : Nous effectuons un test de charge avec uniquement des connexions réussies.
#. Mixte : un mélange de succès et d'échecs : Nous testons avec à la fois des connexions réussies et échouées pour voir comment les échecs affectent les performances.
#. Tous échecs : Nous testons avec uniquement des échecs de connexion et comparons les performances.
Nous aurons besoin d'une application exemple pour le test de charge, la seule différence étant la configuration où nous chargeons defender, axes, ou aucun des deux.
Nous pouvons utiliser un service de test de charge hébergé, ou quelque chose comme jmeter. Quoi qu'il en soit, la méthode doit être cohérente pour tous les tests. Si nous utilisons jmeter, nous devrions fournir notre configuration jmeter pour que d'autres puissent exécuter les tests sur leur propre machine.
Résultats des tests de charge
Nous publierons les résultats ici. Nous expliquerons chaque test, et montrerons les résultats accompagnés de graphiques.
django-axes est excellent mais il stocke tout dans la base de données, ce qui crée un goulot d'étranglement quand vous avez beaucoup de données. Cela ralentit les requêtes d'authentification de 200 à 300 ms. Cela peut être acceptable pour certains sites, mais trop long pour d'autres.
Ce projet a commencé comme un fork de django-axes, en réutilisant autant que possible leur code, en supprimant les parties inutiles et en accélérant les recherches pour améliorer la connexion.
#. Lorsqu'une personne tente de se connecter, on vérifie d'abord si elle est actuellement bloquée. On vérifie le nom d'utilisateur qu'elle essaie d'utiliser, ainsi que l'adresse IP. Si elle est bloquée, passer à l'étape 5. Sinon, passer à l'étape 2.
#. La personne n'est pas bloquée, on vérifie donc si la connexion est valide. Si elle est valide, passer à l'étape 6. Sinon, passer à l'étape 3.
#. La tentative de connexion n'était pas valide. On ajoute son nom d'utilisateur et son adresse IP pour cette tentative dans le cache. Si cela dépasse la limite, on l'ajoute à la liste des bloqués, puis passer à l'étape 5. Si la limite n'est pas dépassée, passer à l'étape 4.
#. La connexion était invalide, mais sans dépasser la limite. La personne est renvoyée vers l'écran de connexion pour réessayer.
#. L'utilisateur est bloqué : Il est redirigé vers une page de blocage l'informant qu'il est bloqué et donnant une estimation du moment où il sera débloqué.
#. La connexion est valide. On réinitialise les tentatives de connexion échouées et on le redirige vers sa destination.
Defender utilise le cache pour enregistrer les tentatives échouées.
Clés de cache
Compteurs :
Booléens (si présent, cela signifie bloqué) :
Vous avez plusieurs options pour personnaliser django-defender. Elles doivent être définies dans votre fichier settings.py.
DEFENDER_LOGIN_FAILURE_LIMIT\ : Int : Le nombre de tentatives de connexion autorisées avant qu'un enregistrement ne soit créé pour les échecs. [Par défaut : 3\ ]
DEFENDER_LOGIN_FAILURE_LIMIT_USERNAME\ : Int : Le nombre de tentatives de connexion autorisées sur un nom d'utilisateur avant qu'un enregistrement ne soit créé pour les échecs. [Par défaut : DEFENDER_LOGIN_FAILURE_LIMIT\ ]
DEFENDER_LOGIN_FAILURE_LIMIT_IP\ : Int : Le nombre de tentatives de connexion autorisées depuis une IP avant qu'un enregistrement ne soit créé pour les échecs. [Par défaut : DEFENDER_LOGIN_FAILURE_LIMIT\ ]
DEFENDER_BEHIND_REVERSE_PROXY\ : Booléen : Y a-t-il un proxy inverse devant defender ? [Par défaut : False\ ]
DEFENDER_REVERSE_PROXY_HEADER\ : Chaîne : Le nom de l'en-tête HTTP contenant l'adresse IP du proxy inverse. [Par défaut : HTTP_X_FORWARDED_FOR\ ]
Justification de l'utilisation de DEFENDER_ATTEMPT_COOLOFF_TIME et DEFENDER_LOCKOUT_COOLOFF_TIME
Bien qu'utiliser DEFENDER_COOLOFF_TIME seul soit suffisant pour la plupart des cas d'usage, dans certains scénarios spécifiques comme un environnement de haute sécurité, les développeurs peuvent souhaiter un contrôle plus fin sur la durée pendant laquelle les tentatives de connexion invalides sont "mémorisées" lors de l'évaluation du verrouillage, par rapport à la durée pendant laquelle ces clés de verrouillage sont effectivement bloquées. DEFENDER_ATTEMPT_COOLOFF_TIME et DEFENDER_LOCKOUT_COOLOFF_TIME permettent cette configuration fine et précise.
Prenons également un exemple de faible sécurité et de faible échelle, comme le site web d'un lycée. Ce site pourrait être hébergé sur quelques ordinateurs de l'école et administré par le personnel informatique et les professeurs d'informatique (si la chance leur sourit). Dans ce scénario, on peut imaginer que de grandes parties du site sont accessibles sans authentification, mais que la connexion donne accès à des informations relativement privilégiées comme le nom, l'email, les notes et l'emploi du temps de l'élève. Enfin, comme un email est lié au compte, on suppose qu'il existe une fonctionnalité de réinitialisation de mot de passe qui débloque le compte une fois effectuée. Dans ce cas, on pourrait estimer qu'il n'est pas nécessaire de mémoriser les échecs de connexion pendant de longues périodes, car l'application souhaite simplement se protéger contre d'éventuelles attaques par déni de service. Cela pourrait être accompli en gardant DEFENDER_ATTEMPT_COOLOFF_TIME bas, disons 30 secondes, et en définissant DEFENDER_LOCKOUT_COOLOFF_TIME à une valeur beaucoup plus élevée, comme 600 secondes. En gardant DEFENDER_ATTEMPT_COOLOFF_TIME bas et en verrouillant les acteurs malveillants pendant des périodes significatives avec DEFENDER_LOCKOUT_COOLOFF_TIME élevé, les attaques rapides par force brute seront contrecarrées et le petit serveur disposera de plus d'espace dans son cache pour d'autres données. Et en offrant une fonctionnalité de réinitialisation de mot de passe comme décrit, ces administrateurs hypothétiques pourraient limiter leur implication dans le déblocage des vrais utilisateurs tout en préservant l'accessibilité souhaitée de leur site.
Bien que l'exemple précédent soit quelque peu artificiel, la pleine puissance de ces configurations est démontrée avec l'explication et l'exemple suivants.
Lorsque DEFENDER_STORE_ACCESS_ATTEMPTS est à True, DEFENDER_LOCKOUT_COOLOFF_TIME peut également être configuré comme une liste d'entiers. Configuré en liste, le nombre de tentatives de connexion échouées précédentes pour la clé de verrouillage configurée est divisé par DEFENDER_LOGIN_FAILURE_LIMIT pour produire un comptage intentionnellement surestimé du nombre d'échecs de connexion pour la période définie par DEFENDER_ACCESS_ATTEMPT_EXPIRATION. Cela finit par être une surestimation car le temps entre les tentatives échouées n'est pas pris en compte dans ce calcul. Bien que cela puisse sembler sévère, dans certains scénarios spécifiques, la protection supplémentaire contre les attaques plus lentes peut valoir\ l'éventuel\ désagrément\ causé aux vrais utilisateurs du système.
Un exemple pourrait être une application web accessible publiquement qui contient des informations sensibles sur ses utilisateurs (disons des relevés financiers personnels). L'application et les données qu'elle contient doivent être accessibles avec un minimum d'interruption, mais la sécurité est essentielle, donc des délais sont tolérés jusqu'à un certain point. Dans ces circonstances, on pourrait être tenté de simplement définir DEFENDER_COOLOFF_TIME sur un très grand entier voire 0 pour une protection maximale. Mais cela signifierait que si un\ vrai\ utilisateur\ est\ effectivement verrouillé, un administrateur devra le débloquer manuellement, ce qui est fastidieux et coûteux. En définissant DEFENDER_ATTEMPT_COOLOFF_TIME sur un nombre suffisamment grand, disons 600, et en définissant DEFENDER_LOCKOUT_COOLOFF_TIME comme une liste d'entiers croissants (par ex. [60, 120, 300, 600, 0]), nous pouvons protéger notre application théorique de manière comparable à si nous avions simplement défini DEFENDER_COOLOFF_TIME sur 600 tout en perturbant nos utilisateurs beaucoup moins.
defender peut être utilisé pour l'authentification autre que le système d'authentification Django. Par exemple, si l'authentification django-rest-framework doit être protégée contre les attaques par force brute, une méthode d'authentification personnalisée peut être implémentée.
Voici un exemple de classe BasicAuthenticationDefender basée sur djangorestframework.BasicAuthentication\ :
.. code-block:: python
import base64 import binascii
from django.utils.translation import gettext_lazy as _
from rest_framework import HTTP_HEADER_ENCODING, exceptions from rest_framework.authentication import ( BasicAuthentication, get_authorization_header, )
from defender import utils from defender import config
class BasicAuthenticationDefender(BasicAuthentication):def get_username_from_request(self, request): auth = get_authorization_header(request).split() return base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':')[0]
def authenticate(self, request): auth = get_authorization_header(request).split()
if not auth or auth[0].lower() != b'basic':
return None
if len(auth) == 1:
msg = _('Invalid basic header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = _('Invalid basic header. Credentials string should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)
if utils.is_already_locked(request, get_username=self.get_username_from_request):
detail = "You have attempted to login {failure_limit} times, with no success." \
"Your account is locked for {cooloff_time_seconds} seconds" \
"".format(
failure_limit=config.FAILURE_LIMIT,
cooloff_time_seconds=config.LOCKOUT_COOLOFF_TIME[
defender_utils.get_lockout_cooloff_time(username=self.get_username_from_request(request))
]
)
raise exceptions.AuthenticationFailed(_(detail))
try:
auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':')
except (TypeError, UnicodeDecodeError, binascii.Error):
msg = _('Invalid basic header. Credentials not correctly base64 encoded.')
raise exceptions.AuthenticationFailed(msg)
userid, password = auth_parts[0], auth_parts[2]
login_unsuccessful = False
login_exception = None
try:
response = self.authenticate_credentials(userid, password)
except exceptions.AuthenticationFailed as e:
login_unsuccessful = True
login_exception = e
utils.add_login_attempt_to_db(request,
login_valid=not login_unsuccessful,
get_username=self.get_username_from_request)
# add the failed attempt to Redis in case of a failed login or resets the attempt count in case of success
utils.check_request(request,
login_unsuccessful=login_unsuccessful,
get_username=self.get_username_from_request)
if login_unsuccessful:
raise login_exception
return response
Pour le faire fonctionner, ajoutez BasicAuthenticationDefender à DEFAULT_AUTHENTICATION_CLASSES avant toutes les autres méthodes d'authentification dans votre settings.py.
defender peut être intégré avec la combinaison de django-rest-framework et django-rest-auth qui permet d'authentifier les utilisateurs.
Références
Voici un exemple de classe BasicAuthenticationDefender basée sur rest_framework.authentication.TokenAuthentication qui utilise la bibliothèque django-rest-auth pour l'authentification des utilisateurs.
.. code-block:: python
import base64 import binascii
from django.conf import settings from django.contrib.auth import get_user_model, authenticate from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm from django.contrib.auth.tokens import default_token_generator from django.utils.http import urlsafe_base64_decode as uid_decoder from django.utils.translation import gettext_lazy as _ from django.utils.encoding import force_str from rest_framework import serializers, exceptions, HTTP_HEADER_ENCODING from rest_framework.exceptions import ValidationError from defender import utils as defender_utils from defender import config from rest_framework.authentication import ( get_authorization_header, )
UserModel = get_user_model()
class BasicAuthenticationDefender(serializers.Serializer):
username = serializers.CharField(required=False, allow_blank=True)
email = serializers.EmailField(required=False, allow_blank=True)
password = serializers.CharField(style={'input_type': 'password'})
def authenticate(self, **kwargs):
request = self.context['request']
if hasattr(settings, 'ACCOUNT_AUTHENTICATION_METHOD'):
login_field = settings.ACCOUNT_AUTHENTICATION_METHOD
else:
login_field = 'username'
userid = self.username_from_request(request, login_field)
if defender_utils.is_already_locked(request, username=userid):
detail = "You have attempted to login {failure_limit} times with no success. "
.format(
failure_limit=config.FAILURE_LIMIT,
cooloff_time_seconds=config.LOCKOUT_COOLOFF_TIME[defender_utils.get_lockout_cooloff_time(username=userid)]
)
raise exceptions.AuthenticationFailed(_(detail))
login_unsuccessful = False
login_exception = None
try:
response = authenticate(request, **kwargs)
if response == None:
login_unsuccessful = True
msg = _('Unable to log in with provided credentials.')
# raise exceptions.ValidationError(msg)
login_exception = exceptions.ValidationError(msg)
except exceptions.AuthenticationFailed as e:
login_unsuccessful = True
login_exception = e
defender_utils.add_login_attempt_to_db(request,
login_valid=not login_unsuccessful,
username=userid)
user_not_blocked = defender_utils.check_request(request,
login_unsuccessful=login_unsuccessful,
username=userid)
if user_not_blocked and not login_unsuccessful:
return response
raise login_exception
def _validate_email(self, email, password):
user = None
if email and password:
user = self.authenticate(email=email, password=password)
else:
msg = _('Must include "email" and "password".')
raise exceptions.ValidationError(msg)
return user
def _validate_username(self, username, password):
user = None
if username and password:
user = self.authenticate(username=username, password=password)
else:
msg = _('Must include "username" and "password".')
raise exceptions.ValidationError(msg)
return user
def _validate_username_email(self, username, email, password):
user = None
if email and password:
user = self.authenticate(email=email, password=password)
elif username and password:
user = self.authenticate(username=username, password=password)
else:
msg = _('Must include either "username" or "email" and "password".')
raise exceptions.ValidationError(msg)
return user
def validate(self, attrs):
username = attrs.get('username')
email = attrs.get('email')
password = attrs.get('password')
user = None
if 'allauth' in settings.INSTALLED_APPS:
from allauth.account import app_settings
# Authentication through email
if app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.EMAIL:
user = self._validate_email(email, password)
# Authentication through username
elif app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.USERNAME:
user = self._validate_username(username, password)
# Authentication through either username or email
else:
user = self._validate_username_email(username, email, password)
else:
# Authentication without using allauth
if email:
try:
username = UserModel.objects.get(
email__iexact=email).username()
except UserModel.DoesNotExist:
pass
if username:
user = self._validate_username_email(username, '', password)
# Did we get back an active user?
if user:
if not user.is_active:
msg = _('User account is disabled.')
raise exceptions.ValidationError(msg)
else:
msg = _('Unable to log in with provided credentials.')
raise exceptions.ValidationError(msg)
# If required, is the email verified?
if 'rest_auth.registration' in settings.INSTALLED_APPS:
from allauth.account import app_settings
if app_settings.EMAIL_VERIFICATION == app_settings.EmailVerificationMethod.MANDATORY:
email_address = user.emailaddress_set.get(email=user.email)
if not email_address.verified:
raise serializers.ValidationError(
_('E-mail is not verified.'))
attrs['user'] = user
return attrs
def username_from_request(self, request, login_field):
user_data = request._data
return user_data[login_field]
Pour le faire fonctionner, ajoutez BasicAuthenticationDefender au dictionnaire REST_AUTH_SERIALIZERS dans votre settings.py sous la clé LOGIN_SERIALIZER.
Par exemple, dans votre settings.py ajoutez la ligne suivante :
.. code-block:: python
REST_AUTH_SERIALIZERS = { 'LOGIN_SERIALIZER': '.BasicAuthenticationDefender', }
defender peut être adapté pour la PasswordResetView de Django afin d'éviter trop de soumissions.
Nous devons créer de nouvelles vues qui sous-classent les vues intégrées de Django LoginView, PasswordResetView et PasswordResetConfirmView — puis utiliser ces vues dans notre urls.py en remplacement de celles intégrées.
Les vues bloquent en fonction de l'adresse e-mail soumise dans le formulaire de réinitialisation du mot de passe. Ceci est différent de l'implémentation par défaut (qui utilise le nom d'utilisateur), donc nous devons faire attention à nettoyer après nous lors de la connexion et de la réinitialisation du mot de passe terminée.
.. code-block:: python
from defender import utils as def_utils
from django.contrib.auth import views as auth_views
class UserSignIn(auth_views.LoginView):
def form_valid(self, form):
"""Force clear all the cached Defender statues for the authenticated user’s email address."""
super_valid = super().form_valid(form)
def_utils.check_request(self.request, False, username=form.get_user().email)
return super_valid
class PasswordResetBruteForceProtectedView(auth_views.PasswordResetView):
def get(self, request, *args, **kwargs):
"""Confirm the user isn’t already blocked by IP before showing the password reset view."""
if def_utils.is_already_locked(request):
return def_utils.lockout_response(request)
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
"""
Confirm the user isn’t already blocked by IP before allowing form POST.
Also, force log this form POST as a single entry in the Defender cache, against the submitted email address.
"""
if def_utils.is_already_locked(request):
return def_utils.lockout_response(request)
def_utils.check_request(
request, login_unsuccessful=True, username=request.POST.get("email")
)
return super().post(request, *args, **kwargs)
class PasswordResetConfirmBruceForceProtectedView(auth_views.PasswordResetConfirmView):
def get(self, request, *args, **kwargs):
"""Confirm the user isn’t already blocked by IP before showing the password confirm view."""
if def_utils.is_already_locked(request):
return def_utils.lockout_response(request)
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
"""Confirm the user isn’t already blocked by IP before allowing form POST for the password change confirmation."""
if def_utils.is_already_locked(request):
return def_utils.lockout_response(request)
return super().post(request, *args, **kwargs)
def form_valid(self, form):
"""Force clear all the cached Defender statues for the user’s email address after successfully changing their password."""
super_valid = super().form_valid(form)
def_utils.check_request(
self.request, login_unsuccessful=False, username=self.user.email
)
return super_valid
django-defender envoie des signaux lors du blocage d'un nom d'utilisateur ou d'une adresse IP. Pour configurer les fonctions de récepteur de signaux :
.. code-block:: python
from django.dispatch import receiver
from defender import signals
@receiver(signals.username_block) def username_blocked(username, **kwargs): print("%s was blocked!" % username)
@receiver(signals.ip_block) def ip_blocked(ip_address, **kwargs): print("%s was blocked!" % ip_address)
Les tests peuvent être exécutés, après avoir cloné le dépôt et avoir Django installé, comme suit :
.. code-block:: bash
PYTHONPATH=$PYTHONPATH:$PWD django-admin test defender --settings=defender.test_settings
Avec la couverture de code :
.. code-block:: bash
PYTHONPATH=$PYTHONPATH:$PWD coverage run --source=defender $(which django-admin) test defender --settings=defender.test_settings
#. python setup.py sdist
#. twine upload dist/*
DEFENDER_LOCK_OUT_BY_IP_AND_USERNAME\ : Booléen : Verrouille un utilisateur en fonction de la combinaison IP et nom d'utilisateur. Cela empêche un utilisateur de bloquer l'accès à l'application pour tous les autres utilisateurs derrière la même adresse IP. [Par défaut : False\ ]
DEFENDER_DISABLE_IP_LOCKOUT\ : Booléen : Si True, ne verrouille pas l'adresse IP de l'utilisateur, seul le nom d'utilisateur sera verrouillé. [Par défaut : False]
DEFENDER_DISABLE_USERNAME_LOCKOUT\ : Booléen : Si True, ne verrouille pas les noms d'utilisateur, seules les adresses IP seront verrouillées. [Par défaut : False]
DEFENDER_COOLOFF_TIME\ : Int : Si défini, définit une période d'inactivité après laquelle les anciennes tentatives échouées et les verrouillages de nom d'utilisateur/IP seront oubliés. Un entier, interprété en secondes. Si 0, ni les tentatives échouées ni les verrouillages n'expirent. [Par défaut : 300\ ]
DEFENDER_ATTEMPT_COOLOFF_TIME\ : Int : Si défini, remplace la période d'inactivité après laquelle les anciennes tentatives échouées seront oubliées, définie par DEFENDER_COOLOFF_TIME. Un entier, interprété en secondes. Si 0, les tentatives échouées n'expirent pas. [Par défaut : DEFENDER_COOLOFF_TIME\ ]
DEFENDER_LOCKOUT_COOLOFF_TIME\ : Int ou Liste : Si défini, remplace la période d'inactivité après laquelle les verrouillages de nom d'utilisateur/IP seront oubliés, définie par DEFENDER_COOLOFF_TIME. Un entier, interprété en secondes. Une liste d'entiers, interprétés comme un nombre de secondes pour les utilisateurs, l'indice de l'entier correspondant au nombre de verrouillages précédents (jusqu'à un maximum) survenus au cours des DEFENDER_ACCESS_ATTEMPT_EXPIRATION dernières heures. Si la propriété est définie sur 0 ou [], le verrouillage n'expire pas. [Par défaut : DEFENDER_COOLOFF_TIME\ ]
DEFENDER_LOCKOUT_TEMPLATE\ : Chaîne : [Par défaut : None\ ] Si défini, spécifie un template à afficher lorsqu'un utilisateur est verrouillé. Le template reçoit les variables de contexte suivantes :
cooloff_time_seconds\ : Le temps de refroidissement en secondescooloff_time_minutes\ : Le temps de refroidissement en minutesfailure_limit\ : Le nombre d'échecs avant blocage.DEFENDER_USERNAME_FORM_FIELD\ : Chaîne : Le nom du champ de formulaire contenant les noms d'utilisateur. [Par défaut : username\ ]
DEFENDER_CACHE_PREFIX\ : Chaîne : Le préfixe de cache pour les clés defender. [Par défaut : defender\ ]
DEFENDER_LOCKOUT_URL\ : Chaîne : L'URL vers laquelle rediriger si une personne est verrouillée.
DEFENDER_REDIS_URL\ : Chaîne : L'URL Redis pour defender. [Par défaut : redis://localhost:6379/0\ ]
(Exemple avec mot de passe : redis://:mypassword@localhost:6379/0\ )
DEFENDER_REDIS_PASSWORD_QUOTE\ : Booléen : Si des caractères spéciaux sont dans le mot de passe Redis (comme '@'), on peut citer le mot de passe avec urllib.parse.quote("password!@#"), et mettre à True. [Par défaut : False\ ]
DEFENDER_REDIS_NAME\ : Chaîne : Le nom du cache depuis CACHES dans vos paramètres Django (par ex. "default"). Si défini, DEFENDER_REDIS_URL sera ignoré. [Par défaut : None\ ]
DEFENDER_STORE_ACCESS_ATTEMPTS\ : Booléen : Si vous souhaitez stocker la tentative de connexion dans la base de données, mettez à True. Si False, elle n'est pas sauvegardée. [Par défaut : True\ ]
DEFENDER_USE_CELERY\ : Booléen : Si vous souhaitez utiliser Celery pour stocker la tentative de connexion dans la base de données, mettez à True. Si False, elle est sauvegardée de manière synchrone. [Par défaut : False\ ]
DEFENDER_ACCESS_ATTEMPT_EXPIRATION\ : Int : Durée en heures pendant laquelle les enregistrements de tentatives de connexion sont conservés avant que la commande de gestion ne les nettoie. [Par défaut : 24\ ]
DEFENDER_GET_USERNAME_FROM_REQUEST_PATH\ : Chaîne : Le chemin d'importation de la fonction qui accède au nom d'utilisateur depuis la requête. Si vous souhaitez utiliser une fonction personnalisée pour accéder et traiter le nom d'utilisateur depuis la requête, vous pouvez la spécifier ici. [Par défaut : defender.utils.username_from_request\ ]