Django XSS in HTML Email Body via EmailMessage

MEDIUM

User input flows into HTML email body content without sanitization, enabling HTML injection in emails.

Rule Information

Language
Python
Category
Django
Author
Shivasurya
Shivasurya
Last Updated
2026-03-22
Tags
pythondjangoxsshtml-injectionemailemailmessagetaint-analysisinter-proceduralCWE-79OWASP-A03
CWE References

Interactive Playground

Experiment with the vulnerable code and security rule below. Edit the code to see how the rule detects different vulnerability patterns.

pathfinder scan --ruleset python/PYTHON-DJANGO-SEC-060 --project .
1
2
3
4
5
6
7
8
9
10
11
rule.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

About This Rule

Understanding the vulnerability and how it is detected

This rule detects HTML injection vulnerabilities in Django applications where untrusted user input from HTTP request parameters flows into HTML email body content constructed for Django's EmailMessage class without proper sanitization.

When user-controlled data is included in HTML email content without escaping, attackers can inject malicious HTML that renders when the recipient opens the email. While modern webmail clients (Gmail, Outlook.com) strip JavaScript from email HTML for security, HTML injection without script execution is still exploitable for phishing attacks -- injecting fake login forms, manipulating the visual appearance of the email, or adding malicious links that lead to credential theft.

Additionally, some desktop email clients and corporate email systems render HTML more permissively, potentially allowing JavaScript execution. The safe approach is to escape user input in HTML emails using the same techniques used for web output.

Security Implications

Potential attack scenarios if this vulnerability is exploited

1

Phishing via Email HTML Injection

An attacker who controls any text that appears in an HTML email can inject HTML to overlay fake content on top of the legitimate email body. Adding a fake "Verify your password" form with an attacker-controlled action URL inside a legitimate-looking company email is a highly effective phishing vector.

2

Malicious Link Injection

HTML injection in emails allows inserting hyperlinks with deceptive display text but attacker-controlled href attributes. Recipients who see a link labeled "Reset your password here" may not check the actual URL, particularly on mobile devices where URLs are hidden.

3

JavaScript Execution in Permissive Email Clients

While major webmail services strip scripts, some corporate email servers, Outlook desktop client configurations, and older email systems render HTML with JavaScript. Organizations with mixed email client environments should treat email HTML injection with the same severity as browser XSS.

4

Content Spoofing and Brand Impersonation

An attacker who triggers an application to send an HTML email with injected content can make legitimate company emails appear to contain fraudulent information, damaging brand trust and potentially creating legal liability.

How to Fix

Recommended remediation steps

  • 1Render HTML emails using Django template files which auto-escape all {{ variable }} interpolations by default.
  • 2When constructing HTML email content in Python code, escape all user-controlled values with django.utils.html.escape() before inclusion.
  • 3Use EmailMultiAlternatives to send both a plain text and HTML version; generate the plain text version from the escaped HTML using strip_tags().
  • 4Validate email addresses with Django's EmailValidator before using them as recipients.
  • 5Consider using a dedicated email templating service that enforces sanitization, or a library like premailer for CSS inlining that also performs escaping.

Detection Scope

How Code Pathfinder analyzes your code for this vulnerability

This rule performs inter-procedural taint analysis with global scope. Sources include calls("request.GET.get"), calls("request.POST.get"), calls("request.GET.__getitem__"), calls("request.POST.__getitem__"), calls("request.body"), and calls("request.read"). Sinks include the body argument of calls("EmailMessage") and calls("EmailMultiAlternatives") when content_subtype is 'html', and the attach_alternative() call with 'text/html' mimetype (tracked via .tracks(0)). Sanitizers include calls("django.utils.html.escape"), calls("django.template.loader.render_to_string"), and calls("bleach.clean").

Compliance & Standards

Industry frameworks and regulations that require detection of this vulnerability

CWE Top 25
CWE-79 ranked #2 in 2023 Most Dangerous Software Weaknesses
OWASP Top 10
A03:2021 - Injection (XSS and HTML injection)
PCI DSS v4.0
Requirement 6.2.4 - protect against injection attacks
NIST SP 800-53
SI-10: Information Input Validation; SI-15: Information Output Filtering
CAN-SPAM Act
Accurate header information and prohibition of deceptive content in commercial emails

References

External resources and documentation

Similar Rules

Explore related security rules for Python

Frequently Asked Questions

Common questions about Django XSS in HTML Email Body via EmailMessage

HTML injection in emails is less severe than browser XSS because email clients generally strip JavaScript. However, it is still a medium-severity finding because: (1) phishing via HTML injection is highly effective, (2) some email clients execute JavaScript, and (3) HTML injection undermines user trust in application emails. It should be fixed using the same escaping discipline as web XSS.
Yes. render_to_string() with a template file auto-escapes all {{ variable }} interpolations, just like rendering web views with render(). The vulnerability only arises when HTML email content is constructed with Python string operations (f-strings, concatenation) that include unescaped user data.
Email header injection is a separate vulnerability (CWE-93) where user input in the To, CC, or Subject headers contains newline characters that inject additional headers. Django's EmailMessage validates and sanitizes these fields, but you should still validate email addresses with EmailValidator before using them as recipients.
Plain text email bodies (content_subtype='plain', the default) do not render HTML, so HTML injection is not applicable. However, user input in plain text emails can still be used for social engineering. For plain text emails, ensure that user input does not contain misleading content.
Process WYSIWYG editor output through bleach.clean() with a strict allowlist of permitted tags and attributes before including it in HTML emails. This permits formatting (bold, italic, links) while removing script tags, event handlers, and other dangerous HTML. Always specify strip=True in bleach.clean() to remove disallowed elements rather than escaping them.

New feature

Get these findings posted directly on your GitHub pull requests

The Django XSS in HTML Email Body via EmailMessage rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.

See how it works