django-axes Documentation

repository·master·Indexed 23 days ago

https://github.com/jazzband/django-axes

A security plugin for Django that tracks suspicious login attempts and provides mechanisms to block brute-force attacks. It includes features for monitoring authentication attempts, managing lockouts via management commands (such as axes_reset_ip and axes_reset_username), and configurable lockout parameters based on IP address, username, or user agent.

Tokens
12.1K
Snippets
29
Records
71
Agent score
80%

What's inside django-axes

  1. Overview of django-axes functionality

    master

    django-axes is a Django plugin designed to monitor suspicious login attempts and implement brute-force attack blocking. It tracks login attempts and prevents further attempts once a configured limit is exceeded.

    Key capabilities include:

    • Tracking Methods: Monitor attempts by IP address, username, user agent, or combinations thereof.
    • Persistence Options: Store attempt data indefinitely in the database or use a fast, DDoS-resistant cache implementation.
    • Access Management: Supports cool-off periods, IP address allow-listing/block-listing, and user account allow-listing.
  2. Configure rolling window expiration with AXES_USE_ATTEMPT_EXPIRATION

    master

    By default, AXES_COOLOFF_TIME acts as an inactivity period: attempts are only cleared if no new failures occur within that time.

    If you set AXES_USE_ATTEMPT_EXPIRATION = True, the behavior changes to a rolling window. In this mode, each failed attempt expires individually after the AXES_COOLOFF_TIME has passed. This is useful for implementing policies like "allow only 3 failed login attempts per 15 minutes".

  3. Implement progressive lockouts with AXES_LOCKOUT_TIERS

    master

    Instead of a single failure limit, you can use AXES_LOCKOUT_TIERS to define multiple levels of punishment. This setting accepts a list of LockoutTier instances. When active, the lowest tier threshold becomes the effective failure limit, and subsequent tiers apply progressively longer cool-off periods.

    Example: 3 failures results in a 15-minute lockout, 6 failures results in a 2-hour lockout, and 10 failures results in a 24-hour lockout.

    from datetime import timedelta
    from axes.conf import LockoutTier
    
    AXES_LOCKOUT_TIERS = [
        LockoutTier(failures=3, cooloff=timedelta(minutes=15)),
        LockoutTier(failures=6, cooloff=timedelta(hours=2)),
        LockoutTier(failures=10, cooloff=timedelta(days=1)),
    ]
  4. Extend django-axes via base classes

    master

    django-axes is designed to be extensible. You can customize its behavior by specializing the following base classes:

    • axes.handlers.base: Base classes for implementing custom logic for handling authentication attempts and failures.
    • axes.backends: Base classes for implementing custom storage backends (e.g., for storing login attempt data in different databases or caches).
    • axes.middleware: Base classes for implementing custom middleware to intercept requests and apply axes logic.

    Alternatively, you can use third-party modules as long as they implement the same APIs provided by these base classes.

  5. How Axes handles authentication failures and lockouts

    master

    Axes uses a combination of exceptions, signals, and middleware to manage security:

    • Blocking: When an authentication attempt violates a rule, AxesBackend raises a PermissionDenied exception. This stops further authentication backends from running and triggers the user_login_failed signal.
    • Monitoring: Axes tracks failed attempts by monitoring the user_login_failed signal. It can track failures based on the attempt IP address, username, user agent, or a combination of all three.
    • Lockout Enforcement: When a lockout rule is triggered, Axes marks the request with a special attribute. The AxesMiddleware then intercepts this request and returns the appropriate lockout response to the user.

    Note for Developers: Axes assumes that login views either call the authenticate method or otherwise notify Axes of attempts/failures using standard Django authentication signals.

  6. How Django Axes augments the Django authentication flow

    master

    Django Axes integrates with the standard Django authentication framework by adding three specific layers to the login process to monitor attempts and enforce lockouts:

    1. Authentication Backend (AxesBackend): This custom backend checks every authentication request. If a request matches a lockout rule (such as a blacklisted IP or exceeding maximum attempts), it raises a PermissionDenied exception to block the attempt.
    2. Signal Receivers: Axes listens for the user_login_failed signal. It records failures from both AxesBackend and other authentication backends, tracking metadata like IP address, username, and user agent.
    3. Middleware (AxesMiddleware): If a request is identified as being under a lockout, the middleware detects a special attribute set on the request and returns a lockout response to the user.

    Axes is designed to be compatible with standard Django implementations and is extensible for 3rd party packages like Django REST Framework, Django Allauth, and Python Social Auth.

  7. Configure django-axes in Django settings

    master

    To integrate django-axes into your Django project, follow these four steps:

    1. Add to INSTALLED_APPS: Add 'axes' to your list. It can be in any position.
    2. Configure AUTHENTICATION_BACKENDS: Add 'axes.backends.AxesStandaloneBackend' as the first item in the list. This ensures Axes processes authentication attempts before the default ModelBackend.
    3. Add AxesMiddleware: Add 'axes.middleware.AxesMiddleware' to your MIDDLEWARE list. It should be the last middleware in the list. This middleware handles formatting lockout messages and rendering responses. If you use custom views and do not want Axes to override authentication responses, you can skip this step.
    4. Sync Database: Run migrations to create the necessary tables.

    Note on Backends: AxesStandaloneBackend is recommended if you have custom logic overriding standard permissions. For backwards compatibility, AxesBackend can be used, but it includes ModelBackend functionality internally.

    INSTALLED_APPS = [
        ...,
        'axes',
    ]
    
    AUTHENTICATION_BACKENDS = [
        'axes.backends.AxesStandaloneBackend',
        'django.contrib.auth.backends.ModelBackend',
    ]
    
    MIDDLEWARE = [
        ...,
        'axes.middleware.AxesMiddleware',
    ]
  8. Ensure the `request` object is passed to `authenticate()`

    master

    For django-axes to monitor login attempts correctly, the request object must be passed as a keyword argument to the Django authenticate() method. This allows Axes to identify the client IP and other request-specific metadata.

    If you are writing custom login views or tests, ensure you include request=request in the call.

    def custom_login_view(request):
        username = ...
        password = ...
    
        user = authenticate(
            request=request,  # this is the important custom argument
            username=username,
            password=password,
        )
    
        if user is not None:
            login(request, user)
  9. Integrate Django Axes with Django REST Framework

    master

    Axes supports Django REST Framework (DRF) authentication schemes that rely on Django's authenticate() function. It does not currently support other schemes like TokenAuthentication.

    To enable lockout functionality in DRF, you must connect DRF to Axes via lockout signals. When a user is locked out, you should catch the user_locked_out signal and raise a rest_framework.exceptions.PermissionDenied exception to return an HTTP 403 response.

    Ensure your signals are loaded in your application's ready() method.

    # example/signals.py
    from django.dispatch import receiver
    from axes.signals import user_locked_out
    from rest_framework.exceptions import PermissionDenied
    
    @receiver(user_locked_out)
    def raise_permission_denied(*args, **kwargs):
        raise PermissionDenied("Too many failed login attempts")
    
    # example/apps.py
    from django import apps
    
    class AppConfig(apps.AppConfig):
        name = "example"
    
        def ready(self):
            from example import signals  # noqa
  10. Configure dedicated caches for Axes

    master

    If you use AxesCacheHandler, the cache must be application-wide. Using local caches like LocMemCache or FileBasedCache can cause unpredictable behavior (e.g., lockouts not being recognized across different Django processes).

    If you cannot change your project's default cache, you can define a specific cache for Axes in two steps:

    1. Add a new cache to your CACHES dictionary.
    2. Point AXES_CACHE to that cache name.
    # 1. Add an extra cache to CACHES
    CACHES = {
        'axes': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': '127.0.0.1:11211',
        }
    }
    
    # 2. Tell Axes to use this cache
    AXES_CACHE = 'axes'
  11. Configure common lockout strategies in django-axes

    master

    You can define different lockout behaviors by combining AXES_FAILURE_LIMIT, AXES_COOLOFF_TIME, and AXES_USE_ATTEMPT_EXPIRATION.

    • Classic Lockout: A fixed number of failures triggers a lockout for a specific duration.
    • Rolling Window: Limits the number of failures allowed within any given time period (requires AXES_USE_ATTEMPT_EXPIRATION = True).
    • Hard Lockout: A fixed number of failures triggers a lockout that requires manual reset (set AXES_COOLOFF_TIME = None).
    # Classic: 3 failures -> 30 min lockout
    AXES_FAILURE_LIMIT = 3
    AXES_COOLOFF_TIME = timedelta(minutes=30)
    
    # Rolling window: max 5 failures in any 15-minute period
    AXES_FAILURE_LIMIT = 5
    AXES_COOLOFF_TIME = timedelta(minutes=15)
    AXES_USE_ATTEMPT_EXPIRATION = True
    
    # Hard lockout (manual reset only)
    AXES_FAILURE_LIMIT = 5
    AXES_COOLOFF_TIME = None
  12. Integrate Django Axes with Django Simple Captcha

    master

    Axes supports django-simple-captcha by allowing you to redirect locked-out users to a page where they can solve a captcha to reset their request status.

    1. Set AXES_LOCKOUT_URL to the path of your captcha view.
    2. In your captcha view, use axes.utils.reset_request(request) after a successful captcha validation to clear the lockout for that request.
    # settings.py
    AXES_LOCKOUT_URL = '/locked'
    
    # example/views.py
    from axes.utils import reset_request
    from django.http import HttpResponseRedirect
    from django.shortcuts import render
    from django.urls import reverse_lazy
    from .forms import AxesCaptchaForm
    
    def locked_out(request):
        if request.POST:
            form = AxesCaptchaForm(request.POST)
            if form.is_valid():
                reset_request(request)
                return HttpResponseRedirect(reverse_lazy('auth_login'))
        else:
            form = AxesCaptchaForm()
    
        return render(request, 'accounts/captcha.html', {'form': form})