5 Ways to Make Django Admin Safer

The more powerful your Django admin is, the safer it should be


In this article I present 5 ways to protect the Django Admin from human errors and attackers.

Change the URL

Every framework has a fingerprint and Django is no exception. A skilled developer, an attacker or even a tech savvy user can identify a Django site by looking at things like cookies and auth URLs.

Once a site is identified as a Django site, an attacker will most likely try /admin.

To make it harder to gain access we can change the "recommended" URL to something harder to guess.

In the base url.py of the app, register the admin site under a different url:

urlpatterns += i18n_patterns(
    url(r'^super-secret/', admin.site.urls, name='admin'),
)

Change "super-secret" to something you and your team can remember.

This is definitely not the only precaution you should take, but it is a good start.


Visually Distinguish Environments

Users and admins are not perfect and mistakes happen. When you have multiple environments such as development, QA, staging and production, it's not unlikely for an admin to perform a destructive operation in the wrong environment by accident (just ask gitlab).

To reduce the chance of mistakes, we mark different environments clearly in the admin:

Indicator in different environments
Indicator in different environments

First you need to have some way of knowing which environment you are on. We have a variable called ENVIRONMENT_NAME we populate during deployment. We have another variable called ENVIRONMENT_COLOR for the indicator color.

To add the environment indicator to every page in the admin, override the base admin template:

<!-- app/templates/admin/base_site.html -->

{% extends "admin/base_site.html" %}

{% block extrastyle %}

<style type="text/css">
    body:before {
        display: block;
        line-height: 35px;
        text-align: center;
        font-weight: bold;
        text-transform: uppercase;
        color: white;
        content: "{{ ENVIRONMENT_NAME }}";
        background-color: {{ ENVIRONMENT_COLOR }};
    }
</style>

{% endblock %}

To make the ENVIRONMENT variables from settings.py available in the template we use a context processor:

# app/context_processors.py

from django.conf import settings

def from_settings(request):
    return {
        'ENVIRONMENT_NAME': settings.ENVIRONMENT_NAME,
        'ENVIRONMENT_COLOR': settings.ENVIRONMENT_COLOR,
    }

To register the context processor add the following in settings.py:

TEMPLATES = [{
    # ...
    'OPTIONS': {
        'context_processors': [
            # ...
            'app.context_processors.from_settings',
        ],
    # ...
    },
}]

Now when you open Django Admin you should see the indicator on top.


Name Your Admin Site

If you have multiple Django services that look the same, admins can easily get confused. To help admins be more aware of where they are, change the title:

# urls.py

from django.contrib import admin

admin.site.site_header = 'Awesome Inc. Administration'
admin.site.site_title = 'Awesome Inc. Administration'

And you get:

Django Admin site with a name and a title
Django Admin site with a name and a title

For more exotic options look around in the docs.


Separate the Django Admin From The Main Site

Using the same codebase you can deploy two instances of the same Django app - one only for the admin and one only for the rest of the app.

This is controversial and not as easy as the other tips. The implementation is dependent on the configuration (e.g. if you are using gunicorn or uwsgi) so I won't go into the details.

Some reasons you might want to split the admin to its own instance are:

  • Deploy the admin inside a VPN (virtual private network) - If the admin is used only internally and you have a VPN it is good practice to have it inside the private network.
  • Remove unnecessary components from the main site - For example, the Django admin uses the messages framework. If the main site does not, you can remove that middleware. Another example is authentication - if the main site is an API backend using token authentication, you can remove a lot of templates configuration, session middleware, etc. and trim some fat from the request-response cycle.
  • Stronger authentication - If you want to strengthen the security of Django Admin you might want to provide a different authentication mechanism just for the admin. This is much easier on different instances with different settings.

We split the admin from the main site only in public facing sites. We don't bother with internal apps because it complicates the deployment and it has no benefit of being more secure.


Add Two Factor Authentication (2FA)

Two factor authentication became very popular lately as many sites started offering this option. 2FA performs authentication using two things:

  1. Something you know - Usually a password.
  2. Something you have - Usually a mobile app that generates a random number every 30 seconds (such as Authenticator by Google).

On first signup the user is usually asked to scan a barcode with the authenticator app. After this initial setup the app will start generating the one-time codes.

I don't usually recommend third party packages, but a couple of months ago we started using django-otp to implement 2FA in our admin site and it's working great for us. It's hosted on Bitbucket so you might have missed it.

The setup is pretty simple:

$ pip install django-otp
$ pip install qrcode

Add django-otp to the installed apps and the middleware:

# settings.py

INSTALLED_APPS = (
    'django_otp',
    'django_otp.plugins.otp_totp',
)

MIDDLEWARE = (
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django_otp.middleware.OTPMiddleware',
)

Name the issuer - this is the name users will see in the authenticator app, so make it distinguishable.

# settings.py

OTP_TOTP_ISSUER = 'Awesome Inc.'

Add 2FA authentication to the admin site:

# urls.py

from django_otp.admin import OTPAdminSite

admin.site.__class__ = OTPAdminSite

Now you have a secure admin page that looks like this:

Django Admin login with OTP token
Django Admin login with OTP token

To set up a new user create a "TOTP Device" from the Django Admin. Once you are done click the QR link and you will get a screen like that:

QR for setting up a new user
QR for setting up a new user

Have the user scan the QR code with the authenticator app on their personal device, and they will have a fresh code generated every 30 seconds.


Final Words

Making a Django admin safer and more secure doesn't have to be hard - you just have to pay attention. Some of the tips mentioned here are very easy to set up and they go a long way.



Similar articles