Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
django-defender — Un'app django riutilizzabile semplice e super veloce che blocca le persone dai tentativi di login con forza bruta. | Kitploit
Strumenti/GitHubGitHub/jazzband/django-defender
Strumenti DifensiviAttacchi alle PasswordSicurezza WebAutenticazione
GitHubjazzband/django-defender

django-defender

Un'app django riutilizzabile semplice e super veloce che blocca le persone dai tentativi di login con forza bruta.

Vedi Repository
1.1k14447 mesi faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

django-defender

.. 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: Versioni Python supportate :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: Versioni Django supportate

.. 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: Copertura

.. image:: https://readthedocs.org/projects/django-defender/badge/?version=latest :alt: Stato della documentazione :target: https://django-defender.readthedocs.io/en/latest/?badge=latest

Un'app riutilizzabile Django semplice che impedisce attacchi di forza bruta ai tentativi di login. L'obiettivo è renderla il più veloce possibile, per non rallentare i tentativi di accesso.

Utilizzeremo una cache in modo da non dover interrogare il database a ogni tentativo di login. La prima versione sarà basata su Redis, ma l'obiettivo è renderlo configurabile in modo che le persone possano usare il backend più adatto alle loro esigenze.

Siti che usano django-defender

Se usi defender sul tuo sito, invia una PR per aggiungerlo all'elenco.

  • https://hub.docker.com
  • https://www.mycosbuilder.com

Documentazione

La documentazione è disponibile su Read the Docs:

https://django-defender.readthedocs.io

Caratteristiche

  • Registra tutti i tentativi di login nel database

  • Supporto per proxy inversi con diverse intestazioni per gli indirizzi IP

  • Limite di frequenza basato su

    • Nome utente
    • Indirizzo IP
  • Usa Redis per la lista nera

  • Configurazione

    • Server Redis

      • Host
      • Porta
      • Database
      • Password
      • Prefisso chiave
    • Durata del blocco

    • Numero di tentativi errati prima del blocco

  • Copertura del codice al 95%

  • Documentazione completa

  • Possibilità di memorizzare i tentativi di login nel database

  • Comando di amministrazione per pulire la tabella dei tentativi di login

  • Pagine di amministrazione

    • Elenco di nomi utente e indirizzi IP bloccati
    • Elenco dei tentativi di login recenti
    • Possibilità di sbloccare le persone
  • Può essere facilmente adattato a metodi di autenticazione personalizzati.

  • Vengono inviati segnali quando si blocca un nome utente o un IP

Pagine di amministrazione


.. 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

Requisiti

  • Python: 3.8, 3.9, 3.10, 3.11, 3.12, PyPy
  • Django: 3.2, 4.2, 5.0, 5.1, 5.2
  • Redis: 5.x, 6.x, 7.x

Installazione

Scarica il codice ed esegui il setup in uno dei seguenti modi, a seconda del metodo.

Per installare la versione pronta per la produzione da PyPI:

.. code-block:: bash

pip install django-defender

Per installare la versione di sviluppo dal codice sorgente dopo il download:

.. code-block:: bash

python setup.py install

Per installare la versione di sviluppo del branch master dal repository GitHub:

.. code-block:: bash

pip install -e git+http://github.com/kencochran django-defender.git#egg=django_defender-dev

Prima di tutto, devi aggiungere questo progetto alla tua lista di INSTALLED_APPS in settings.py

.. code-block:: python

INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', # ... 'defender', # ... ]

Successivamente, installa il middleware FailedLoginMiddleware

.. code-block:: python

MIDDLEWARE_CLASSES = [ 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'defender.middleware.FailedLoginMiddleware', ]

Se vuoi gestire gli utenti bloccati tramite l'amministrazione di Django, aggiungi quanto segue al tuo urls.py

.. code-block:: python

urlpatterns = [ path('admin/defender/', include('defender.urls')), # admin defender path('admin/', admin.site.urls), # admin normale # i tuoi pattern seguono... ]

Migrazioni


Dovrai creare le tabelle nel tuo database necessarie per il funzionamento.

.. code-block:: bash

python manage.py migrate defender

Comandi di gestione


cleanup_django_defender

Se hai un sito web con molto traffico, la tabella AccessAttempts si riempirà abbastanza rapidamente. Se non hai bisogno di conservare i dati a fini di audit, c'è un comando di gestione per aiutarti a mantenerla pulita.

Esaminerà l'impostazione DEFENDER_ACCESS_ATTEMPT_EXPIRATION per determinare quali record verranno eliminati. Il valore predefinito, se non specificato, è 24 ore.

.. code-block:: bash

$ python manage.py cleanup_django_defender

Puoi impostarlo come cron job giornaliero o settimanale per mantenere la dimensione della tabella ridotta.

.. code-block:: bash

eseguito alle 00:24 ogni mattina.

24 0 * * * /usr/bin/python manage.py cleanup_django_defender >> /var/log/django_defender_cleanup.log

Obiettivi a lungo termine

  • Backend componibili, in modo che le persone possano usare qualcosa di diverso da Redis
  • Inviare email agli utenti quando il loro account viene bloccato
  • Aggiungere una whitelist per nomi utente e IP che non verranno mai bloccati (amministratori, ecc.)
  • Aggiungere una lista nera permanente per gli indirizzi IP
  • Scansionare per proxy IP conosciuti e non bloccare le richieste provenienti da quelli (migliorare le possibilità che un buon IP venga bloccato)
  • Aggiungere comando di gestione per eliminare i tentativi di login vecchi (configurabili).

Prestazioni


L'obiettivo di defender è renderlo il più veloce possibile in modo da non rallentare il processo di login. Per assicurarci di raggiungere i nostri obiettivi, abbiamo bisogno di un modo per testare l'applicazione e verificare di essere sulla strada giusta. Il modo migliore per farlo è confrontare la velocità di un normale login Django con defender e django-axes.

Il normale login Django sarebbe la nostra base di riferimento, e ci aspettiamo che sia il più veloce dei 3 metodi, perché non ci sono ulteriori controlli in corso.

Il login con defender sarà molto probabilmente più lento del login Django, e si spera più veloce del login django-axes. L'obiettivo è rendere la differenza il più piccola possibile tra il login normale e defender.

La velocità del login django-axes sarà probabilmente la più lenta dei tre poiché esegue più controlli e molte query al database.

Il modo migliore per determinare la velocità di un login è fare un test di carico contro un applicazione con ciascuna configurazione e confrontare i tempi di login per ogni tipo.

Test di carico


Per assicurarci di coprire tutti i diversi tipi di login, nel nostro test di carico dobbiamo avere più di un test.

#. Tutti successi: Eseguiremo un test di carico solo con login riusciti.

#. Misto: alcuni successi, alcuni fallimenti: Eseguiremo un test di carico con alcuni login riusciti e alcuni fallimenti per vedere come i fallimenti influenzano le prestazioni.

#. Tutti fallimenti: Eseguiremo un test di carico con tutti login falliti e vedremo la differenza di prestazioni.

Avremo bisogno di un'applicazione di esempio da usare per il test di carico, con l'unica differenza nella configurazione in cui carichiamo defender, axes o nessuno di essi.

Possiamo usare un servizio di test di carico ospitato o qualcosa come jmeter. In ogni caso dobbiamo essere coerenti per tutti i test. Se usiamo jmeter, dovremmo avere la nostra configurazione jmeter in modo che altri possano eseguire i test per proprio conto.

Risultati dei test di carico


Pubblicheremo i risultati qui. Spiegheremo ogni test e mostreremo i risultati insieme ad alcuni grafici.

Perché non django-axes

django-axes è ottimo, ma mette tutto nel database e questo causa un collo di bottiglia quando si hanno molti dati. Rallenta le richieste di autenticazione fino a 200-300ms. Per alcuni siti potrebbe non essere molto, ma per altri è troppo.

Questo è iniziato come un fork di django-axes e utilizza il più possibile il loro codice, rimuovendo le parti non necessarie e accelerando le ricerche per migliorare il login.

Come funziona django-defender

#. Quando qualcuno tenta di accedere, controlliamo prima se è attualmente bloccato. Controlliamo il nome utente che sta provando a usare e l'indirizzo IP. Se è bloccato, vai al punto 5. Se non è bloccato, vai al punto 2.

#. Non è bloccato, quindi controlliamo se il login è valido. Se valido vai al punto 6. Se non valido, vai al punto 3.

#. Il tentativo di login non era valido. Aggiungi il suo nome utente e indirizzo IP per questo tentativo alla cache. Se questo lo porta oltre il limite, aggiungilo alla lista bloccati, quindi vai al punto 5. Se non supera il limite, vai al punto 4.

#. Il login non era valido, ma non è stato superato il limite. Rimandalo alla schermata di login per riprovare.

#. L'utente è bloccato: invialo alla pagina di blocco, dicendogli che è bloccato e dandogli una stima su quando verrà sbloccato.

#. Il login è valido. Reimposta eventuali tentativi di login falliti e reindirizza alla loro destinazione.

Backend della cache

Defender utilizza la cache per salvare i tentativi falliti.

Chiavi della cache


Contatori:

  • prefix:failed:ip:[ip] (conteggio, TTL)
  • prefix:failed:username:[username] (conteggio, TTL)

Booleani (se presente, è bloccato):

  • prefix:blocked:ip:[ip] (true, TTL)
  • prefix:blocked:username:[username] (true, TTL)

Personalizzare django-defender

Hai a disposizione alcune opzioni per personalizzare un po' django-defender. Queste devono essere definite nel tuo file settings.py.

  • DEFENDER_LOGIN_FAILURE_LIMIT\ : Int: Il numero di tentativi di login consentiti prima di creare un record per i login falliti. [Default: 3\ ]

  • DEFENDER_LOGIN_FAILURE_LIMIT_USERNAME\ : Int: Il numero di tentativi di login consentiti su un nome utente prima di creare un record per i login falliti. [Default: DEFENDER_LOGIN_FAILURE_LIMIT\ ]

  • DEFENDER_LOGIN_FAILURE_LIMIT_IP\ : Int: Il numero di tentativi di login consentiti da un IP prima di creare un record per i login falliti. [Default: DEFENDER_LOGIN_FAILURE_LIMIT\ ]

  • DEFENDER_BEHIND_REVERSE_PROXY\ : Booleano: Defender è dietro un proxy inverso? [Default: False\ ]

  • DEFENDER_REVERSE_PROXY_HEADER\ : Stringa: il nome dell'intestazione http con il tuo indirizzo IP del proxy inverso [Default: HTTP_X_FORWARDED_FOR\ ]

  • DEFENDER_LOCK_OUT_BY_IP_AND_USERNAME\ : Booleano: Blocca un utente in base a una combinazione di IP e nome utente. Questo impedisce a un utente di negare l'accesso all'applicazione per tutti gli altri utenti che accedono dallo stesso indirizzo IP. [Default: \ ]

Razionale per l'uso di DEFENDER_ATTEMPT_COOLOFF_TIME e DEFENDER_LOCKOUT_COOLOFF_TIME


Sebbene l'uso di DEFENDER_COOLOFF_TIME da solo sia sufficiente per la maggior parte dei casi d'uso, quando si utilizza defender in alcuni scenari specifici come in un contesto di alta sicurezza, gli sviluppatori potrebbero desiderare un controllo più granulare su per quanto tempo vengono "ricordati" i tentativi di login non validi mentre sono in considerazione per il blocco rispetto al tempo in cui quelle chiavi di blocco sono effettivamente bloccate dal sistema. DEFENDER_ATTEMPT_COOLOFF_TIME e DEFENDER_LOCKOUT_COOLOFF_TIME consentono proprio questa configurazione granulare.

Possiamo anche prendere un esempio a bassa sicurezza e bassa scala come il sito web di una scuola superiore. Un tale sito potrebbe essere gestito su alcuni computer della scuola e amministrato dal personale IT della scuola e dagli insegnanti di informatica (se abbastanza fortunati da averne). In questo scenario possiamo immaginare che ci siano porzioni significative del sito web accessibili senza autenticazione, ma accedere al sito potrebbe fornire accesso a informazioni relativamente privilegiate come nome, email, voti e orario delle lezioni dello studente. Infine, poiché c'è un'email collegata all'account, assumeremo che esista una funzionalità di reimpostazione della password che sblocca l'account al completamento. In tal caso, si potrebbe immaginare che non sia necessario ricordare i login falliti per lunghi periodi di tempo poiché l'applicazione desidererebbe semplicemente proteggersi da potenziali attacchi denial of service. Ciò potrebbe essere ottenuto mantenendo DEFENDER_ATTEMPT_COOLOFF_TIME basso, diciamo 30 secondi, e impostando DEFENDER_LOCKOUT_COOLOFF_TIME a qualcosa di molto più alto come 600 secondi. Mantenendo DEFENDER_ATTEMPT_COOLOFF_TIME basso e bloccando i malintenzionati per periodi di tempo significativi impostando DEFENDER_LOCKOUT_COOLOFF_TIME alto, gli attacchi di forza bruta rapidi verranno comunque sconfitti e il loro piccolo server avrà più spazio nella cache per altri dati. E fornendo la funzionalità di reimpostazione della password come descritto sopra, questi ipotetici amministratori potrebbero limitare il loro coinvolgimento richiesto nello sbloccare utenti reali mantenendo al contempo l'accessibilità prevista del loro sito web.

Sebbene l'esempio precedente sia un po' artificioso, la piena potenza di queste configurazioni è dimostrata con la seguente spiegazione ed esempio.

Quando DEFENDER_STORE_ACCESS_ATTEMPTS è True, DEFENDER_LOCKOUT_COOLOFF_TIME può anche essere configurato come una lista di interi. Quando configurato come lista, il numero di tentativi di login falliti precedenti per la chiave di blocco configurata viene diviso per DEFENDER_LOGIN_FAILURE_LIMIT per produrre un conteggio intenzionalmente sovrastimato del numero di login falliti per il periodo definito da DEFENDER_ACCESS_ATTEMPT_EXPIRATION. Questo risulta essere una sovrastima perché il tempo tra i tentativi di login falliti non viene considerato quando si esegue questo calcolo. Sebbene possa sembrare severo, in alcuni scenari specifici la protezione aggiuntiva contro attacchi più lenti può valere il potenziale disagio causato agli utenti reali del sistema.

Un esempio di ciò potrebbe essere un'applicazione web pubblica accessibile che ospita informazioni sensibili dei suoi utenti (diciamo registri finanziari personali). L'applicazione e i dati in essa contenuti dovrebbero essere accessibili con interruzioni minime, tuttavia la sicurezza è integrale, quindi i ritardi possono essere tollerati fino a un certo punto. In queste circostanze potremmo desiderare di impostare semplicemente DEFENDER_COOLOFF_TIME a un intero molto grande o addirittura a 0 per la massima protezione. Ma questo significherebbe che se un utente reale viene bloccato dal sistema, avremo bisogno di un amministratore per sbloccarlo manualmente, il che è ovviamente scomodo e costoso. Impostando DEFENDER_ATTEMPT_COOLOFF_TIME a un numero sufficientemente grande, ad esempio 600, e impostando DEFENDER_LOCKOUT_COOLOFF_TIME a una lista di interi crescenti (es. [60, 120, 300, 600, 0]) possiamo proteggere la nostra applicazione teorica in modo comparabile a se avessimo semplicemente impostato DEFENDER_COOLOFF_TIME a 600 disturbando i nostri utenti in modo significativamente minore.

Adattamento ad altri metodi di autenticazione

defender può essere utilizzato per l'autenticazione diversa dal sistema di autenticazione Django. Ad esempio, se l'autenticazione django-rest-framework deve essere protetta da attacchi di forza bruta, è possibile implementare un metodo di autenticazione personalizzato.

C'è una classe campione BasicAuthenticationDefender basata su 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]

root@kitploit:~
   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

Per farlo funzionare, aggiungi BasicAuthenticationDefender a DEFAULT_AUTHENTICATION_CLASSES sopra tutti gli altri metodi di autenticazione nel tuo settings.py.

Adattamento ad altri metodi di autenticazione :- django-rest-auth in djangorestframework

defender può essere integrato con la combinazione di django-rest-framework e django-rest-auth che possono essere utilizzati per autenticare gli utenti.

Riferimento


  • https://www.django-rest-framework.org/
  • https://django-rest-auth.readthedocs.io/en/latest/

Di seguito è riportato un esempio della classe BasicAuthenticationDefender basata su rest_framework.authentication.TokenAuthentication che utilizza la libreria django-rest-auth per l'autenticazione degli utenti.

.. 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, )

Get the UserModel

UserModel = get_user_model()

class BasicAuthenticationDefender(serializers.Serializer):

root@kitploit:~
  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]

Per farlo funzionare, aggiungi BasicAuthenticationDefender al dizionario REST_AUTH_SERIALIZERS nel tuo settings.py sotto la chiave LOGIN_SERIALIZER. Ad esempio, nel tuo settings.py aggiungi la riga seguente,

.. code-block:: python

REST_AUTH_SERIALIZERS = { 'LOGIN_SERIALIZER': '.BasicAuthenticationDefender', }

Adattamento per i moduli di reimpostazione della password

defender può essere adattato per il PasswordResetView di Django per prevenire troppi invii.

Dobbiamo creare alcune nuove viste che ereditano da LoginView, PasswordResetView e PasswordResetConfirmView integrati di Django — quindi utilizzare queste viste nel nostro urls.py come sostituti di quelle integrate.

Le viste bloccano in base all'indirizzo email inserito nel modulo di reimpostazione password. Questo è diverso dall'implementazione predefinita (che utilizza il nome utente), quindi dobbiamo fare attenzione a pulire dopo di noi al login e al completamento della reimpostazione password.

.. code-block:: python

root@kitploit:~
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

Segnali Django

django-defender invierà segnali quando blocca un nome utente o un indirizzo IP. Per impostare le funzioni riceventi dei segnali:

.. 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)

Esecuzione dei test

I test possono essere eseguiti, dopo aver clonato il repository e aver installato Django, in questo modo:

.. code-block:: bash

PYTHONPATH=$PYTHONPATH:$PWD django-admin test defender --settings=defender.test_settings

Con copertura del codice:

.. code-block:: bash

PYTHONPATH=$PYTHONPATH:$PWD coverage run --source=defender $(which django-admin) test defender --settings=defender.test_settings

Rilascio

#. python setup.py sdist #. twine upload dist/*

Scarica lo strumento
False
  • DEFENDER_DISABLE_IP_LOCKOUT\ : Booleano: Se True, non bloccherà l'indirizzo IP dell'utente, bloccherà solo il nome utente. [Default: False]

  • DEFENDER_DISABLE_USERNAME_LOCKOUT\ : Booleano: Se True, non bloccherà i nomi utente, bloccherà solo gli indirizzi IP. [Default: False]

  • DEFENDER_COOLOFF_TIME\ : Int: Se impostato, definisce un periodo di inattività dopo il quale i vecchi tentativi di login falliti e i blocchi di nome utente/IP verranno dimenticati. Un intero, sarà interpretato come un numero di secondi. Se 0, né i tentativi di login falliti né i blocchi di nome utente/IP scadranno. [Default: 300\ ]

  • DEFENDER_ATTEMPT_COOLOFF_TIME\ : Int: Se impostato, sovrascrive il periodo di inattività dopo il quale i vecchi tentativi di login falliti verranno dimenticati impostato da DEFENDER_COOLOFF_TIME. Un intero, sarà interpretato come un numero di secondi. Se 0, i tentativi di login falliti non scadranno. [Default: DEFENDER_COOLOFF_TIME\ ]

  • DEFENDER_LOCKOUT_COOLOFF_TIME\ : Int o Lista: Se impostato, sovrascrive il periodo di inattività dopo il quale i blocchi di nome utente/IP verranno dimenticati impostato da DEFENDER_COOLOFF_TIME. Un intero, sarà interpretato come un numero di secondi. Una lista di interi, sarà interpretata come un numero di secondi per utenti con l'indice dell'intero che rappresenta il numero di blocchi precedenti (fino a un massimo) avvenuti nelle ultime DEFENDER_ACCESS_ATTEMPT_EXPIRATION ore. Se la proprietà è impostata a 0 o [], il blocco di nome utente/IP non scadrà. [Default: DEFENDER_COOLOFF_TIME\ ]

  • DEFENDER_LOCKOUT_TEMPLATE\ : Stringa: [Default: None\ ] Se impostato, specifica un template da renderizzare quando un utente viene bloccato. Il template riceve le seguenti variabili di contesto:

    • cooloff_time_seconds\ : Il tempo di raffreddamento in secondi
    • cooloff_time_minutes\ : Il tempo di raffreddamento in minuti
    • failure_limit\ : Il numero di fallimenti prima di venire bloccati.
  • DEFENDER_USERNAME_FORM_FIELD\ : Stringa: il nome del campo del form che contiene il nome utente. [Default: username\ ]

  • DEFENDER_CACHE_PREFIX\ : Stringa: Il prefisso della cache per le tue chiavi defender. [Default: defender\ ]

  • DEFENDER_LOCKOUT_URL\ : Stringa: L'URL a cui reindirizzare se qualcuno è bloccato.

  • DEFENDER_REDIS_URL\ : Stringa: l'url redis per defender. [Default: redis://localhost:6379/0\ ] (Esempio con password: redis://:mypassword@localhost:6379/0\ )

  • DEFENDER_REDIS_PASSWORD_QUOTE\ : Booleano: se ci sono caratteri speciali nella password di redis (come '@'), possiamo quotare la password urllib.parse.quote("password!@#") e impostare a True. [Default: False\ ]

  • DEFENDER_REDIS_NAME\ : Stringa: il nome della cache da CACHES nelle tue impostazioni Django (es. "default"). Se impostato, DEFENDER_REDIS_URL verrà ignorato. [Default: None\ ]

  • DEFENDER_STORE_ACCESS_ATTEMPTS\ : Booleano: Se vuoi memorizzare il tentativo di login nel database, impostalo a True. Se False, non viene salvato [Default: True\ ]

  • DEFENDER_USE_CELERY\ : Booleano: Se vuoi usare Celery per memorizzare il tentativo di login nel database, impostalo a True. Se False, viene salvato inline. [Default: False\ ]

  • DEFENDER_ACCESS_ATTEMPT_EXPIRATION\ : Int: Periodo di tempo in ore per quanto mantenere i record dei tentativi di accesso nel database prima che il comando di gestione li pulisca. [Default: 24\ ]

  • DEFENDER_GET_USERNAME_FROM_REQUEST_PATH\ : Stringa: Il percorso di importazione della funzione che ottiene il nome utente dalla richiesta. Se vuoi usare una funzione personalizzata per ottenere ed elaborare il nome utente dalla richiesta - puoi specificarla qui. [Default: defender.utils.username_from_request\ ]