An In-Depth walkthrough of Djangos Default Middleware

This is a very in-depth walkthrough of what the different pieces of Djangos default MiddleWare does, and how it relates to django-rest-framework. Most of this isn’t particularly relevant to know unless you’re debugging a Middleware issue — in that case it’s invaluable information. Middleware issues can be tricky to debug, especially if the issue changes, depending on the order of the MiddleWare. We’ll also go over how the Middleware interacts with Django-rest-framework, when there’s a difference between that and vanilla Django.

How does Middleware work?

Django’s middleware is an onion-y layer that’s around the actual view code you write. It has the ability to alter the request going in, such as setting properties on it or issuing redirects. It also has the ability to alter the response going out, such as adding headers to it, or modifying the response body.

They also have another ability, which is where the onion metaphor kind of breaks down. Middleware can also intercept a request right before control is handed to a view. This is used if the Middleware needs to perform different actions, depending on the actual view being served. We’ll see this used primarily in the .

Django’s built-in middleware

This is the stack of middleware that a Django project comes with by default. A request is passed down the line, and a response is passed up. This means, that the topmost middleware gets to modify the request first, and the response last.

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

We’ll go through each of these pieces of middleware in order, and describe what they do on the way in — meaning how it modifies the request, and on the way out, aka how it modifies the response. If the Middleware does something before control is handed to the view, we’ll also look at that.

SecurityMiddleware [ docs ] [ source ]

The SecurityMiddleware provides several independent security enhancements, like HTTPS redirect, and a bunch of security headers.

On the way in

Checks whether or not the request should be an HTTPS request, and if it should be, and isn’t, it returns a redirect instead of the regular response.

On the way out

Sets a bunch of security based headers, like , XSS protection headers and so forth.

SessionMiddleware [docs] [source]

The SessionMiddleware is the middleware that allows Django to hold persistent Sessions for each user.

On the way in

Fetches the cookie from and asks the SessionEngine for any data related to this session id. It then assigns this retrieved session data to

On the way out

The Middleware checks whether or not the session needs to be deleted or updated in the database. It checks whether or not the session is completely empty, in which case it deletes the sessionId cookie. If the session has been modified, or SESSION_SAVE_EVERY_REQUEST is set, it updates the session in the database, and gives the client a new cookie to use for the next request.

CommonMiddleware [docs] [source]

CommonMiddleware combines different unrelated functionality. It does URL rewriting based on the and settings. It blocks access to and sets the header for responses

On the way in

Checks for forbidden user agents in , and if it finds a match, it simply returns a 403.

If is set to true, it adds a trailing slash to a request and redirects, if doing so turns an invalid path into a valid one.

If is set to true, it prepends to the URL and issues a redirect.

On the way out

For 404 pages, it checks whether or not the 404 url needs an appended slash, and appends it if it does. For non-streaming responses, it adds the Content-Length header.

CsrfViewMiddleware [docs] [source]

This is probably the most complex middleware that comes with Django. Its purpose is to prevent against Cross Site Request Forgery.

On the way in

Gets the csrf token for the current request. This can either be from a cookie or, if is set to true, from the SessionStorage. Sets to the token, if it exists.

Before the view is processed

First it checks whether or not the request has set to . If it is, it skips validation. is set to after a Request has passed through the CsrfViewMiddleware. Django has decorators that also provide csrf protection, such as csrf_protect. To avoid doing csrf checks twice, the middleware and the decorator both set to .

Next it handles the @csrf_exempt decorator. This decorator can be applied to a view, to ignore CSRF protections for this view. The decorator simply sets the attribute on the view. The Middleware then checks for this attribute on the view, and if it exists, it lets the request through.

At this point the Middleware has decided whether or not it needs to do Csrf validation. The actual Csrf validation is as follows:

If the request uses a “safe” verb such as , there's no further validation needed. But for "unsafe" HTTP methods such as , or additional validation is done.

The first thing it does, is check whether or not the request has the attribute . This attribute is set by the Django testing framework, where the Django Test Client marks requests with this attribute, so you don't have to deal with CSRF in tests.

Next up, if you’re on a SSL connection, it does some checks, such as detecting whether we were redirected from a unsecure site to a secure one, and rejects the request if that's the case.

If we pass here, we retrieve the from where we previously set it. If there's no , we reject the request.

The next thing it does is, it compares the cookie with the that you must provide in forms. It does this by reading request.POST, with all of the implications this entails. If it can't find the in the POST payload, it tries to get it from the X-CSRFToken header. It then compares the cookie token with the token from the header or the payload. If the csrf tokens are identical, it will let the request through, if not, it will reject it.

On the way out

It does some check to figure out whether or not it should set a new cookie. Primarily it checks for request.META['CSRF_COOKIE_USED'] - and if it has been used, we renew it.

is set when a CSRF token is used, such as when using the {% csrf_token %} tag amongst others.

Integration with Django-Rest-Framework

has an interesting integration with the CsrfViewMiddleware. It wraps every view in the decorator, meaning the CsrfViewMiddleware ignores it. This is so DRF can support multiple authentication backends. In session-based authentications, DRF then runs its own copy of CsrfViewMiddleware to ensure that the Csrf tokens are as they should be.

AuthenticationMiddleware [docs] [source]

The AuthenticationMiddleware sets the property on an incoming request, to either an , or a logged in-user, fetched from an Authentication Backend.

On the way in

It sets to a LazyObject - meaning it's never instantiated before it's read.

When request.user is evaluated, it’s fetched from an via a session key based on the users primary key. When the user logs in, these keys are saved in the Django validates that the session hash matches the , this hash changes when the user changes password, meaning that when the user changes password, the session is invalidated.

On the way out

It does nothing.

MessageMiddleware [docs] [source]

On the way in

This sets to an instance of whatever is. By default this is the SessionStorage. In your code, when you call or similar methods, these just add the messages to

On the way out

This takes any messages that have been stored on the request, but not yet “used” and saves them to the backend. The backend is usually the , but it can also be cookie-based, which means the messages are saved on the object.

XFrameOptionsMiddleware [docs] [source]

On the way in

It does nothing.

On the way out

This sets some headers which prevent clickjacking. It only sets the header if it's not already set, or the view has not been wrapped in the decorator. This decorator works the same way as , by setting an attribute on the view, that the middleware checks for.

Originally published at https://www.gustavwengel.dk.

Software Developer at SCADA Minds