Code Monkey home page Code Monkey logo

django-defender's Introduction

django-defender

A simple django reusable app that blocks people from brute forcing login attempts. The goal is to make this as fast as possible, so that we do not slow down the login attempts.

We will use a cache so that it doesn't have to hit the database in order to check the database on each login attempt. The first version will be based on Redis, but the goal is to make this configurable so that people can use what they want for a backend, so it is configurable.

Version 0.1 will be very limited in features, it will only do a few things, but the goal is to do those things very well, and have full unit tests with docs.

Build status

Build Status Coverage StatusCode Health

Goals for 0.1

  • Log all login attempts to the database
  • support for reverse proxies with different headers for IP addresses
  • rate limit based on:
    • username
    • ip address
  • use redis for the blacklist
  • configuration
    • redis server
      • host
      • port
      • database
      • password
      • key_prefix
    • block length
    • number of incorrect attempts before block
  • 100% code coverage
  • full documentation
  • admin pages
    • list of blocked usernames and ip's
    • ability to unblock people
    • list of recent login attempts
    • search by username for recent login attempts

Long term goals

  • pluggable backends, so people can use something other then redis.
  • email users when their account is blocked
  • add a whitelist for username and ip's that we will never block (admin's, etc)
  • add a permanent black list
    • ip address
  • scan for known proxy ip's and don't block requests coming from those (improve the chances that a good IP is blocked)
  • add management command to prune old (configurable) login attempts.

Performance:

The goal of defender is to make it as fast as possible so that it doesn't slow down the login process. In order to make sure our goals are met we need a way to test the application to make sure we are on the right track. The best way to do this is to compare how fast a normal Django login takes with defender and django-axes.

The normal django login, would be our baseline, and we expect it to be the fastest of the 3 methods, because there are no additional checks happening.

The defender login would most likely be slower then the django login, and hopefully faster then the django-axes login. The goal is to make it as little of a difference between the regular raw login, and defender.

The django-axes login speed, will probably be the slowest of the three since it does more checks and does a lot of database queries.

The best way to determine the speed of a login is to do a load test against an application with each setup, and compare the login times for each type.

Types of Load tests

In order to make sure we cover all the different types of logins, in our load test we need to have more then one test.

  1. All success:
  • We will do a load test with nothing but successful logins
  1. Mixed: some success some failure:
  • We will load test with some successful logins and some failures to see how the failure effect the performance.
  1. All Failures:
  • We will load test with all failure logins and see the difference in performance.

We will need a sample application that we can use for the load test, with the only difference is the configuration where we either load defender, axes, or none of them.

We can use a hosted load testing service, or something like jmeter. Either way we need to be consistent for all of the tests. If we use jmeter, we should have our jmeter configuration for others to run the tests on their own.

Results

We will post the results here. We will explain each test, and show the results along with some charts.

Why not django-axes

django-axes is great but it puts everything in the database, and this causes a bottle neck when you have a lot of data. It slows down the auth requests by as much as 200-300ms. This might not be much for some sites, but for others it is too long.

This started out as a fork of django-axes, and is using as much of their code as possible, and removing the parts not needed, and speeding up the lookups to improve the login.

requirements

  • django: 1.6.x, 1.7.x
  • redis
  • python: 2.6.x, 2.7.x, 3.3.x, 3.4.x, PyPy

How it works

  1. When someone tries to login, we first check to see if they are currently blocked. We check the username they are trying to use, as well as the IP address. If they are blocked, goto step 10. If not blocked go to step 2.

  2. They are not blocked, so we check to see if the login was valid. If valid go to step 20. If not valid go to step 3.

  3. Login attempt wasn't valid. Add their username and IP address for this attempt to the cache. If this brings them over the limit, add them to the blocked list, and then goto step 10. If not over the limit goto step 4.

  4. login was invalid, but not over the limit. Send them back to the login screen to try again.

  5. User is blocked: Send them to the blocked page, telling them they are blocked, and give an estimate on when they will be unblocked.

  6. Login is valid. Reset any failed login attempts, and forward to their destination.

Cache backend:

  • ip_attempts (count, TTL)
  • username_attempts (count, TTL)
  • ip_blocks (list)
  • username_blocks (list)

cache keys:

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

Rate limiting Example

# example of how to do rate limiting by IP
# assuming it is 10 requests being the limit
# this assumes there is a DECAY of DECAY_TIME
# to remove invalid logins after a set number of time
# For every incorrect login, we reset the block time.

FUNCTION LIMIT_API_CALL(ip)
current = LLEN(ip)
IF current > 10 THEN
    ERROR "too many requests per second"
ELSE
    MULTI
        RPUSH(ip, ip)
        EXPIRE(ip, DECAY_TIME)
    EXEC
END

Installing Django-defender

Download code, and run setup.

TODO: add to pypi once stable.

    $ python setup.py install

    or

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

First of all, you must add this project to your list of INSTALLED_APPS in settings.py::

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    ...
    'defender',
    ...
    )

Next, install the FailedLoginMiddleware middleware::

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

Database tables:

You will need to create tables in your database that are necessary for operation.

If you're using Django 1.7.x:

python manage.py migrate defender

On versions of Django prior to 1.7, you might use South (version >= 1.0).

python manage.py migrate defender

If you're not using South, a normal syncdb will work:

python manage.py syncdb

Customizing Defender

You have a couple options available to you to customize django-defender a bit. These should be defined in your settings.py file.

  • DEFENDER_LOGIN_FAILURE_LIMIT: Int: The number of login attempts allowed before a record is created for the failed logins. Default: 3
  • DEFENDER_USE_USER_AGENT: Boolean: If True, lock out / log based on an IP address AND a user agent. This means requests from different user agents but from the same IP are treated differently. Default: False
  • DEFENDER_COOLOFF_TIME: Int: If set, defines a period of inactivity after which old failed login attempts will be forgotten. An integer, will be interpreted as a number of seconds. If 0, the locks will not expire. Default: 300
  • DEFENDER_LOCKOUT_TEMPLATE: String: If set, specifies a template to render when a user is locked out. Template receives cooloff_time and failure_limit as context variables. Default: None
  • DEFENDER_USERNAME_FORM_FIELD: String: the name of the form field that contains your users usernames. Default: username
  • DEFENDER_REVERSE_PROXY_HEADER: String: the name of the http header with your reverse proxy IP address Default: HTTP_X_FORWARDED_FOR
  • DEFENDER_CACHE_PREFIX: String: The cache prefix for your defender keys. Default: defender
  • DEFENDER_LOCKOUT_URL: String: The URL you want to redirect to if someone is locked out.
  • DEFENDER_REDIS_URL: String: the redis url for defender. Default: redis://localhost:6379/0 (Example with password: redis://:mypassword@localhost:6379/0)
  • DEFENDER_PROTECTED_LOGINS: Tuple: Used by ViewDecoratorMiddleware to decide which login urls need protecting. Default: ('/accounts/login/',)
  • DEFENDER_USE_CELERY: Boolean: If you want to use Celery to store the login attempt to the database, set to True. If False, it is saved inline. Default: False

Running Tests

Tests can be run, after you clone the repository and having Django installed, like:

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

With Code coverage:

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

django-defender's People

Contributors

kencochrane avatar marcusmartins avatar shin- avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.