Security on a Django app – HTTPS everywhere

Authentication credentials between your site and your end users must be secret. Is your site secure enough?

One important step is to use HTTPS everywhere in your Django website, and in this post we will show you how to implement it.

In particular, if you are deploying your App on Heroku, don’t miss this post 😉

From HTTP to HTTPS

Fortunately, all Heroku app domains, like myapp.herokuapp.com, are SSL enabled and can be accessed via https. If you are redirecting the Heroku app to your domain, the configuration can be more complicated — better check the official documentation.

If you maintain the Heroku domain, we just need to redirect all the http requests to be https.

For that matter, we will use the package django-secure, which helps you configure and check some security aspects.

Activate your development environment and install the package with:

$ workon dev_env
$ pip install django-secure

Check the version installed with

$ pip freeze

and add the package in the requirements/common.txt file (as this package will be used for the testing, developing and production environments). We are following the requirements structure of the tutorials in this website, which consists in a common.txt file containing all the packages that are used for all the environments, and a separate file for each environment: test.txt, dev.txt and prod.txt. These files inherit from the common.txt file with the command -r common.txt.

Next we can install the same package with in your testing environment with

$ workon test_env
$ pip install -r requirements/test.txt

Once installed, add it into the settings.py file (if you are using different setting files for each environment, install it in your common settings file):

INSTALLED_APPS = (
    …
    ‘djangosecure’,
    …
)

and add the corresponding middleware (make sure to add it at the beginning, so that it’s the first thing it sees):

MIDDLEWARE_CLASSES = (
    ‘djangosecure.middleware.SecurityMiddleware’,
    …
)

Finally, define the variable:

SECURE_SSL_REDIRECT = True

this will redirect all non-SSL requests to SSL (http to https). If you have a infinite redirects, check this post in the django-secure documentation to solve it. If you are using Heroku though, keep reading.

SSL Django Server

If you want to check if the SSL redirect is working on your local computer, you can install another package, named django-sslserver, that provides an SSL-enabled development server.

$ workon dev_env
$ pip install django-sslserver

Next, check the version installed and add it into the requirements/dev.txt file (not in the common.txt file!). Do the same with the testing environment.

Add it into installed apps, only in development_settings.py and testing_settings.py with:

INSTALLED_APPS += (
    …
    ‘sslserver’,
    …
)

Note the += in the statement. This adds the ‘sslserver’ app to the installed apps defined in the common settings.py file.

Now you can run the SSL enabled server with

$ python manage.py runsslserver

If you have infinite redirects on your local server, you can try to solve it by checking this post in the django-secure documentation. However, I will take another approach.

I will just remove the SSL redirect on both testing and development settings files:

SECURE_SSL_REDIRECT = False

This way, both http and https will be available (one with runserver and the other with runsslserver). If this doesn’t work for you, it might be because of the cache in your browser. Try to access the localhost with a private window (on Mac, type cmd+shift+n to open one).

If you go to your localhost while running the SSL server, you’ll see that you’ve been redirected to the https version. However, your browser will complain because the SSL certificate is home-made and the browser doesn’t trust it.

HTTPS on Heroku

For the particular case of Heroku, you need to add the following setting to avoid the infinite redirects.

SECURE_PROXY_SSL_HEADER = (“HTTP_X_FORWARDED_PROTO”, “https”)

This is because Heroku sets the request header to HTTP_X_FORWARDED_PROTO when the request comes via an https.

Ok, commit your changes and push them into Heroku. Does it work?

Secure Cookies

Sometimes browser send cookies back to the server. We should make sure that this transfer is made under an HTTPS request.

Open again the common settings.py and add the following:

SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

But change them to false in your development and testing settings. Otherwise, you won’t be able to login or send a form with the development server.

HTTP Strict Transport Security

Moreover, django-secure allows you to set the Strict Transport Security header. As I understand, your server tells each browser to only access your site via HTTPS. Therefore, when a user tries to go to your site with an HTTP, the browser changes to HTTPS and makes the request. Note that it is the browser working, not your server!

This prevents attacks from man-in-the-middle, as the ability to intercept requests and responses between the user and your server has been greatly reduced.

To activate HSTS, go to your settings.py file and add

SECURE_HSTS_SECONDS = 31536000 

which indicates browsers that future requests for the next year should use only HTTPS.

If your site has subdomains defined, you may want to add them the header too. You just need:

SECURE_HSTS_INCLUDE_SUBDOMAINS = True

Check your Django Site Security

It’s time to check the security of your Django site! You can do it here:

http://ponycheckup.com

Can you get a 100% rate? 😉

Give it a +1 if you liked it, and share it with your friends! 🙂

Google+TwitterLinkedInFacebookReddit

Please, add +Marina Mele in your comments. This way I will get a notification email and I will answer you as soon as possible! :-)