Skip to content

Instantly share code, notes, and snippets.

@baldurthoremilsson
Created October 10, 2012 14:40
Show Gist options
  • Save baldurthoremilsson/3866049 to your computer and use it in GitHub Desktop.
Save baldurthoremilsson/3866049 to your computer and use it in GitHub Desktop.
HTTPS enforced in Django

HTTPSMiddleware for Django

About

This middlware enforces HTTPS for logged in users and HTTP for others. This can be overridden with the require_https view decorator, which always enforces HTTPS.

It has been tested with Django 1.4.1 running on Python 2.6 and 2.7.

Usage

Put httpsmiddleware.py in to one of your apps and add the HTTPSMiddleware class to your MIDDLEWARE_CLASSES in settings.py:

MIDDLEWARE_CLASSES = (
    ...
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'myapp.httpsmiddlware.HTTPSMiddleware',
    ...
)

Note that HTTPSMiddleware must come after AuthenticationMiddleware. See the Django documentation on middlewares for further information about how the middlewares work.

When you have added the middleware to your project all authenticated users that open a page on your site through HTTP are redirected to HTTPS. To take full advantage of this it is highly recommended that you put the following line in your settings.py

SESSION_COOKIE_SECURE = True

This hides the session cookie when users connect through HTTP, mitigating man in the middle attacks. See then Django documentation on security for more information.

Settings

The HTTPSMiddleware adds a cookie when users log in that indicates that the logged in user is using the website. This cookie is not a secure cookie and can thus be seen when a user opens a page through HTTP. It is neccessary when SESSION_COOKIE_SECURE is set to True and not all requests are redirected to HTTPS.

This cookie is called authenticated_user by default, but this can be overridden in settings.py:

AUTH_COOKIE_NAME = 'new_name'
import time
from django.conf import settings
from django.http import HttpResponseRedirect
from django.utils.http import cookie_date
AUTH_COOKIE_NAME = getattr(settings, 'AUTH_COOKIE_NAME', 'authenticated_user')
def http_redirect(request):
"""Returns an HttpResponseRedirect to the URL of the given request with
the protocol set to http://
"""
absolute_uri = request.build_absolute_uri()
if absolute_uri.startswith('https'):
absolute_uri = 'http%s' % absolute_uri[5:]
return HttpResponseRedirect(absolute_uri)
def https_redirect(request):
"""Returns an HttpResponseRedirect to the URL of the given request with
the protocol set to https://
"""
absolute_uri = request.build_absolute_uri()
if not absolute_uri.startswith('https'):
absolute_uri = 'https%s' % absolute_uri[4:]
return HttpResponseRedirect(absolute_uri)
def require_https(func):
"""A view decorator that enforces HTTPS requests for the given view.
If the view is requested via HTTP it returns an HttpResponseRedirect for
HTTPS.
"""
def inner(request, *args, **kwargs):
if not request.is_secure():
return https_redirect(request)
return func(request, *args, **kwargs)
inner.require_https = True
return inner
class HTTPSMiddleware(object):
"""This middleware ensures that logged in users always connect via HTTPS.
Authenticated users connecting via HTTP are redirected to the same URL via
HTTPS.
Unauthenticated users connecting via HTTPS are redirected to the same
URL via HTTP. This can be overridden with the require_https view decorator,
which enforces HTTPS connections.
Using this middleware with SESSION_COOKIE_SECURE = True in settings.py is
*highly* reccomended.
This middleware needs one setting in settings.py:
AUTH_COOKIE_NAME: name of the cookie used to indicate that a user is
logged in. (When SESSION_COOKIE_SECURE is True the user is "invisible"
when requesting via HTTP, so another non-secure cookie is needed).
"""
def _get_auth_cookie(self, request):
"""Returns the auth cookie if it is present in the given request, and
False otherwise.
"""
return request.COOKIES.get(AUTH_COOKIE_NAME, False)
def process_request(self, request):
"""Ensures that an authenticated user connects via HTTPS."""
auth_cookie = self._get_auth_cookie(request)
if not request.is_secure() and auth_cookie:
return https_redirect(request)
def process_view(self, request, view_func, view_args, view_kwargs):
"""Ensures that only views decorated with the require_https decorator
can be requested via HTTPS by unauthenticated users.
"""
require_https = getattr(view_func, 'require_https', False)
if request.is_secure() and not request.user.is_authenticated() and not require_https:
redirect = http_redirect(request)
redirect.delete_cookie(AUTH_COOKIE_NAME,
path=settings.SESSION_COOKIE_PATH,
domain=settings.SESSION_COOKIE_DOMAIN)
return redirect
def process_response(self, request, response):
"""Ensures that authenticated users can be detected in HTTP requests by
setting the auth cookie.
"""
auth_cookie = self._get_auth_cookie(request)
try:
user = request.user
except AttributeError:
return response
if not auth_cookie and user.is_authenticated():
if request.session.get_expire_at_browser_close():
max_age = None
expires = None
else:
max_age = request.session.get_expiry_age()
expires_time = time.time() + max_age
expires = cookie_date(expires_time)
response.set_cookie(AUTH_COOKIE_NAME, 'true',
max_age=max_age, expires=expires,
domain=settings.SESSION_COOKIE_DOMAIN,
path=settings.SESSION_COOKIE_PATH)
return response
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment