Marco Borgeaud 8209548e54
Diff blocks: fix incorrect use for python (#2795)
Improvement identified in #2790.

Add a prefix to the diff-id when it is used multiple times in different
"how to fix it in XYZ" sections to avoid ambiguity and pedantically
follow the spec:

> A single and unique diff-id should be used only once for each type of
code example as shown in the description of a rule.

Obvious typos around `diff-type` were fixed.

An obvious extra use of diff blocks was removed.
2023-08-21 15:22:49 +02:00

120 lines
3.6 KiB
Plaintext

== How to fix it in Django Templates
=== Code examples
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.
==== Noncompliant code example
[source,python,diff-id=11,diff-type=noncompliant]
----
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,html,diff-id=12,diff-type=noncompliant]
----
<!doctype html>
{% autoescape false %}
{{ hello }} <!-- Noncompliant -->
{% endautoescape %}
----
==== Compliant solution
[source,python,diff-id=11,diff-type=compliant]
----
from django.shortcuts import render
def hello(request):
name = request.GET.get("name")
return render(request, 'hello.html', {'name': name})
----
[source,html,diff-id=12,diff-type=compliant]
----
<!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.
==== Noncompliant code example
[source,python,diff-id=13,diff-type=noncompliant]
----
# settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'OPTIONS': {
'autoescape': False,
],
},
},
]
----
==== Compliant solution
[source,python,diff-id=13,diff-type=compliant]
----
# 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.
===== Noncompliant code example
[source,html,diff-id=14,diff-type=noncompliant]
----
<!doctype html>
<script> var name = '{{ name }}';</script>
----
===== Compliant solution
[source,html,diff-id=14,diff-type=compliant]
----
<!doctype html>
{{ name|json_script:"name-data" }}
<script> var name = JSON.parse(document.getElementById('name-data').textContent);</script>
----
include::../../common/pitfalls/validation.adoc[]
=== Going the extra mile
include::../../common/extra-mile/csp.adoc[]