XSS prevention for Django
This is a cross-site scripting (XSS) prevention cheat sheet by Semgrep, Inc. It contains code patterns of potential XSS in an application. Instead of scrutinizing code for exploitable vulnerabilities, the recommendations in this cheat sheet pave a safe road for developers that mitigate the possibility of XSS in your code. By following these recommendations, you can be reasonably sure your code is free of XSS.
Mitigation summary
In general, always use the template engine provided by Django using render(). If you need HTML escaping, use mark_safe() combined with format_html() and review each individual usage carefully. Once reviewed, mark with # nosem. Beware of putting data in dangerous locations in templates. And as always, run a security checker continuously on your code.
Semgrep ruleset for this cheatsheet: https://semgrep.dev/p/minusworld.django-xss
Check your project using Semgrep
The following command runs an optimized set of rules for your project:
semgrep --config p/default
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.
Example:
mark_safe(html_content)
References:
Mitigation
Ban mark_safe(). Alternatively, if needed, use in combination with format_html() and review each usage carefully. Create an exemption with # nosem.
Semgrep rule
python.django.security.audit.avoid-mark-safe.avoid-mark-safe1.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.
Example:
SafeString(f"<div>{request.POST.get('name')}</div>")
References:
Mitigation
Ban SafeString(). Alternatively, prefer mark_safe() if necessary.