1. Server code: Marking "safe" content, which does not escape HTML
1.A. Using mark_safe()
mark_safe()
marks the returned content as "safe to render." This instructs the template engine
to bypass HTML escaping, creating the possibility of a XSS vulnerability.
Recommendation: If needed, use in combination with format_html()
and review each usage carefully. Create an exemption with # nosem
.
Code example:
mark_safe(html_content)
References:
1.B. Using the SafeString
class directly
The SafeString
class is how Django determines which variables should be
escaped and which should not. Elements passed to mark_safe()
are returned
as a SafeString
. Invoking SafeString
directly will bypass HTML escaping
which could create a XSS vulnerabliity.
Recommendation: Prefer mark_safe()
if necessary.
Code example:
SafeString(f"<div>{request.POST.get('name')}</div>")
References:
1.C. Registering a custom filter with is_safe=True
Registering a filter with is_safe=True
indicates to Django that the filter
absolutely does not introduce any unsafe HTML characters. The value returned
from the filter will be marked as "safe" when the input is also marked "safe".
Generally, this is acceptable, but if you cannot be certain the filter is safe,
it may introduce a XSS vulnerability.
Recommendation: Prefer mark_safe()
if necessary.
Code example:
@register.filter(is_safe=True)
def myfilter(value):
return value
References:
1.D. Use of the __html__
magic method in a class
The __html__
magic method is used by the Django template engine to determine whether the object
should be escaped. If available, the value returned by the method will not be escaped and could
introduce a XSS vulnerability.
Recommendation: Prefer mark_safe()
if necessary.
Code example:
class RawHtml(str):
def __html__(self):
return str(self)
References:
1.E. Using html_safe()
The html_safe()
decorator adds the __html__
magic method to the supplied class.
The added __html__
magic method will return the exact string representation of the class
(e.g., str(self)
). Because objects with the `__html__ method are not escaped, this
could create a XSS vulnerability.
Recommendation: Prefer mark_safe()
if necessary.
2. Server code: Bypassing the template engine
2.A. Directly writing a response using HttpResponse
or similar classes
Writing results directly to HttpResponse
or similar classes bypasses the Django template engine.
This also bypasses the HTML escaping built into the template engine and creates the possibility
of a XSS vulnerability. Use render()
with a template instead.
Recommendation: Use render()
Code example:
return HttpResponse("Hello, " + name)
References:
2.B. Globally disabling autoescape
Autoescaping can be globally disabled in Django settings. This should never be done if you are rendering HTML; now, every response returned to the user will need to be audited to ensure it is free of XSS vulnerabilities.
Recommendation: Do not globally disable escaping. If HTML escaping is necessary, use mark_safe()
.
Code example:
TEMPLATES = [
{
...,
'OPTIONS': {'autoescape': False}
}
]
References:
2.C. Setting autoescape=False
in a template context
Setting autoescape=False
in a template context will disable HTML escaping for
that template. Any data rendered in that template could be a XSS vulnerability.
Recommendation: Use mark_safe()
if necessary
Code example:
response = render(request, "index.html", {"autoescape": False})
References:
3. Templates: unescaped variables
3.A. Use of the | safe
filter
The | safe
filter marks the content as "safe for rendering." This has the same
effect as mark_safe()
in Python code. This will permit direct rendering of HTML
and create a possible XSS vulnerability.
Recommendation: Use mark_safe()
in Python if necessary.
3.B. Use of the | safeseq
filter
The | safeseq
filter marks the content as "safe for rendering." This has the same
effect as mark_safe()
in Python code. This will permit direct rendering of HTML
and create a possible XSS vulnerability.
Recommendation: Use mark_safe()
in Python if necessary.
3.C. The {% autoescape off %}
block
The {$ autoescape off %}
block disables autoescaping for whole portions of the
template. Disabling autoescaping allows HTML characters to be rendered directly onto
the page which could create XSS vulnerabilities.
Recommendation: Use mark_safe()
in Python if necessary.
4. Templates: Variable in dangerous location
4.A. Unquoted variable in HTML attribute
Unquoted template variables rendered into HTML attributes is a potential XSS vector
because an attacker could inject JavaScript handlers which do not require HTML characters.
An example handler might look like: onmouseover=alert(1)
. HTML escaping will not mitigate this.
The variable must be quoted to avoid this.
Recommendation: Always use quotes around HTML attributes.
Code example:
<div class="{{ classes }}"></div>
References:
4.B. Variable in href
attribute
Template variables in a href
value could still accept the javascript:
URI.
This could be a XSS vulnerability. HTML escaping will not prevent this. Use url_for
to generate links.
Recommendation: Use url_for
to generate links.
4.C. Variable in <script>
block
Template variables placed directly into JavaScript or similar are now directly in a code execution context. Normal HTML escaping will not prevent the possibility of code injection because code can be written without HTML characters. This creates the potential for XSS vulnerabilities, or worse.
Recommendation: Use the json_script
template tag and read the data in JavaScript using the element ID.
Code example:
<script>var name = {{ name }};</script>
References:
Mitigations
Item | Name | Semgrep rule | Recommendation |
---|---|---|---|
1.A. | Ban mark_safe() |
python.django.security.audit.avoid-mark-safe.avoid-mark-safe | If needed, use in combination with format_html() and review each usage carefully. Create an exemption with # nosem . |
1.B. | Ban SafeString() |
N/A | Prefer mark_safe() if necessary. |
1.C. | Do not mark filters with is_safe=True . |
python.django.security.audit.xss.filter-with-is-safe | Prefer mark_safe() if necessary. |
1.D. | Ban __html__ in classes |
python.django.security.audit.xss.html-magic-method.html-magic-method | Prefer mark_safe() if necessary. |
1.E. | Ban html_safe() |
python.django.security.audit.xss.html-safe.html-safe | Prefer mark_safe() if necessary. |
2.A. | Ban HttpResponse and similar classes |
python.django.security.audit.xss.direct-use-of-httpresponse | Use render() |
2.B. | Ban globally dissabling autoescape | python.django.security.audit.xss.global-autoescape-off.global-autoescape-off | Do not globally disable escaping. If HTML escaping is necessary, use mark_safe() . |
2.C. | Ban autoescape=False in template contexts |
python.django.security.audit.xss.context-autoescape-off.context-autoescape-off | Use mark_safe() if necessary |
3.A. | Ban | safe |
python.flask.security.xss.audit.template-unescaped-with-safe.template-unescaped-with-safe | Use mark_safe() in Python if necessary. |
3.B. | Ban | safeseq |
python.django.security.audit.xss.template-var-unescaped-with-safeseq.template-var-unescaped-with-safeseq | Use mark_safe() in Python if necessary. |
3.C. | Ban {% autoescape off %} |
python.django.security.audit.xss.template-autoescape-off.template-autoescape-off | Use mark_safe() in Python if necessary. |
4.A. | Flag unquoted HTML attributes with Jinja expressions | python.flask.security.xss.audit.template-unquoted-attribute-var.template-unquoted-attribute-var | Always use quotes around HTML attributes. |
4.B. | Flag template variables in href attributes |
python.django.security.audit.xss.template-href-var.template-href-var | Use url_for to generate links. |
4.C. | Ban template variables in <script> blocks. |
N/A | Use the json_script template tag and read the data in JavaScript using the element ID. |