114 lines
3.2 KiB
Plaintext

=== How to fix it in DTL
The following code is vulnerable to cross-site scripting because auto-escaping of special HTML characters has been disabled. The recommended way to fix this code is to move the HTML content to the template and to only inject the dynamic value. Therefore, it is not necessary to disable auto-escaping.
[cols="a,a"]
|===
|
[source,python]
----
from django.shortcuts import render
def hello(request):
name = request.GET.get("name")
hello = f"<h1>Hello { name }</h1>"
return render(request, 'hello.html', {'hello': hello})
----
|
[source,python]
----
from django.shortcuts import render
def hello(request):
name = request.GET.get("name")
return render(request, 'hello.html', {'name': name})
----
|
[source,html]
----
<!doctype html>
{% autoescape false %}
{{ hello }} <!-- Noncompliant -->
{% endautoescape %}
----
|
[source,html]
----
<!doctype html>
<h1>Hello {{ name }}</h1>
----
|===
=== How does this work?
Template engines are used by web applications to build HTML content. Template files contain both static HTML and template language instructions. These instructions allow, for example, to insert dynamic values in the document as the template is rendered. Template engines can auto-escape HTML special characters of dynamic values in order to prevent XSS vulnerabilities.
In Django applications, the engine's auto-escaping feature is enabled by default. XSS vulnerabilities arise when an untrusted value is injected into the template and auto-escaping is disabled with `++{% autoescape false %}++` or `++|safe++`. This is often the case when a piece of dynamic HTML is generated from code and used in a template variable.
include::../../common/fix/data_encoding.adoc[]
Django template auto-escaping only takes care of HTML entity encoding. It does not protect from XSS when a variable is injected into an unquoted attribute or directly into a script block.
Auto-escaping can also be disabled at the application level and introduce XSS vulnerabilities even if `++{% autoescape false %}++` or `++|safe++` are not used.
[cols="a,a"]
|===
|
[source,python]
----
# settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'OPTIONS': {
'autoescape': False,
],
},
},
]
----
|
[source,python]
----
# settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'OPTIONS': {
'autoescape': True,
],
},
},
]
----
|===
=== Pitfalls
==== Variables in script blocks
As mentioned in the section "How to fix it", injecting user-controlled values into a client-side JavaScript `++script++` is dangerous.
In such a case it is better to add the value to an attribute.
Another option is to use the `++json_script++` filter to insert a data structure that can then be accessed through the JavaScript code.
[cols="a,a"]
|===
|
[source,html]
----
<!doctype html>
<script> var name = '{{ name }}';</script>
----
|
[source,html]
----
<!doctype html>
{{ name\|json_script:"name-data" }}
<script> var name = JSON.parse(document.getElementById('name-data').textContent);</script>
----
|===
include::../../common/pitfalls/validation.adoc[]