Beware of Cross-site scripting (XSS) in Django Templates

1. Django Templates support Automatic HTML escaping. 2. Caveates in Django Templates leading to Cross-site scripting (XSS). 2.1 Safe Filter, 2.2 Unquoted Payload, 2.3 autoescape off 2.4 Variable in script tag

Beware of Cross-site scripting (XSS) in Django Templates
Beware of Cross-site scripting (XSS) in Django Templates

Security is critical when developing web application. Cross-site scripting is one of the most common attacks

Never trust user input.

What is Cross site scripting?

An XSS is a client-side vulnerability where the attacker injects malicious client-side scripts into the vulnerable web applications

Purpose of XSS Attack?

  1. Steal Confidential Information from the User
  2. Remote Code Execution/Evaluation

Django comes with in-built security features to protect you from XSS attacks.

Django Templates support Automatic HTML escaping where all expressions are HTML-escaped by default.

This feature converts certain HTML characters into their HTML code as follows:

  • < is converted to &lt;
  • > is converted to &gt;
  • ‘ (single quote) is converted to &#x27;
  • “ (double quote) is converted to &quot;
  • & is converted to &amp;
Django will by default render it as
<script src="evil">  as  &gt;script src="evil"&lt;
If you’re using Django’s template system, you’re protected except for a few of the cases listed below

Caveats in Django Templates leading to Vulnerable to XSS

Use of Safe Filter

Django provides safe filter with which you can disable escaping for particular data

Syntax: {{ data|safe }}

Lets say data = "<script>alert('1');</script>"
{{ data}} will output  &lt;script&gt;alert(&#x27;1&#x27;);&lt;/script&gt;
{{ data|safe }} will output <script>alert('1');</script>

As data is not escaped, the payload will be executed.

Unquoted Payload

Django’s sanitization does not apply to unquoted data

user.name = {{ username }} ; 

If the attacker can inject any JS payload in the name variable, XSS attack would be executed

Turning autoescape off in template context or Block

This tag takes either on or off. There are 2 ways to turn autoescape on or off.

  1. In Block, using
    {% autoescape off %}
         Hello {{ name }}
    {% endautoescape %}
  2. In Template context,
    response = render(request,"index.html",{"autoescape":False})

Since Autoescape off will disable HTML escaping for that template, any data rendered is vulnerable to XSS.

Variables in href attribute

HTML escaping does not prevent payload execution with javascript: URI. Variables in href attributes accept javascript: URI

For use-cases which allow accept links from users, it is vulnerable to XSS

Mitigation: Validate if url does not start with javascript:
url.lower().strip(' \n\t').startswith("javascript:")

Note: make sure condition is

Variables inside script tag

One of the use-cases of Django Templates is to pass data to the <script> tag. If the data is controllable by the user, it can lead to Cross-Site Scripting.

The safe filter is used inside the script tag so all the HTML entities will be escaped

<script> 
const data = "{{ data|safe }}";
</script>

Hence a payload like

<script> 
    const data = "</script><script src="https://example.com/evil.js"></script>"; 
</script>

could lead to script Injection escalating it to Cross site scripting

The correct way to mitigate it is to use the json_script

> jsonscript -> Returns an object into a JSON object surrounded by <script></script> tags

In the template, we would use it like

{{ data|json_script:"data" }}

For data = "</script><script src="https://example.com/evil.js"></script>"

It would render to

<script id="data" type="application/json">"\u003C/script\u003E\u003Cscript src=\"https://example.com/evil.js\"\u003E\u003C/script\u003E"</script>

But since the type of it is "application/json", the browser won't execute it. At this stage, we have defined the element. The next step would be to get the data from the html element.

{{ data|json_script:"data" }}
<script>
  const data = JSON.parse(document.getElementById('data').textContent);
</script>

Reference: https://adamj.eu/tech/2020/02/18/safely-including-data-for-javascript-in-a-django-template/

Please comment below if you are interested in any of blogs related to XSS

  1. Most critical XSS scenarios
  2. Types of XSS along with examples
  3. How to handle input sanitisation for XSS payloads?
  4. Steps to check if your website is vulnerable to XSS?
  5. How WAF can help prevent XSS?