Re: [Django] #34753: Document how to safely construct email addresses

24 views
Skip to first unread message

Django

unread,
Dec 4, 2025, 6:10:50 PM12/4/25
to django-...@googlegroups.com
#34753: Document how to safely construct email addresses
--------------------------------------+------------------------------------
Reporter: Sylvain Fankhauser | Owner: nobody
Type: Cleanup/optimization | Status: new
Component: Documentation | Version:
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------------+------------------------------------
Comment (by Mike Edmunds):

I would suggest reworking the entire existing "Preventing header
injection" section as part of this change. Both the text and example can
be improved.

A more useful example might be actually treating it as a typical contact
form, with name, email, subject and message fields:
- from_email would be `f'"{name} via contact form" <contact-
fo...@example.com>` (but formatted ''safely'')
- to would be a constant (`["con...@example.com"]` or something like
that)
- reply_to would be `[f"{name} <{email}>"]` (but formatted ''safely'')
- subject & body would come from the form

This corrects another problem in the current example: trying to use an
email from a web form as the from_email. (No email service lets you send
messages ''from'' any random address.)
--
Ticket URL: <https://code.djangoproject.com/ticket/34753#comment:4>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Dec 17, 2025, 12:36:27 AM12/17/25
to django-...@googlegroups.com
#34753: Document how to safely construct email addresses
-------------------------------------+-------------------------------------
Reporter: Sylvain Fankhauser | Owner: Raghav-
Type: | Bodani
Cleanup/optimization | Status: assigned
Component: Documentation | Version:
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Raghav-Bodani):

* owner: nobody => Raghav-Bodani
* status: new => assigned

Comment:

I’m planning to work on updating the email documentation to cover safe
construction of email addresses, incorporating the suggestions above.
--
Ticket URL: <https://code.djangoproject.com/ticket/34753#comment:5>

Django

unread,
Dec 19, 2025, 2:25:16 AM12/19/25
to django-...@googlegroups.com
#34753: Document how to safely construct email addresses
-------------------------------------+-------------------------------------
Reporter: Sylvain Fankhauser | Owner: Raghav
Type: | Bodani
Cleanup/optimization | Status: assigned
Component: Documentation | Version:
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Raghav Bodani):

Quick update: the first draft of the documentation changes is ready. I
will do a final review and open a PR once it’s ready.
--
Ticket URL: <https://code.djangoproject.com/ticket/34753#comment:6>

Django

unread,
Dec 20, 2025, 2:05:23 AM12/20/25
to django-...@googlegroups.com
#34753: Document how to safely construct email addresses
-------------------------------------+-------------------------------------
Reporter: Sylvain Fankhauser | Owner: Raghav
Type: | Bodani
Cleanup/optimization | Status: assigned
Component: Documentation | Version:
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Raghav Bodani):

PR: https://github.com/django/django/pull/20431
--
Ticket URL: <https://code.djangoproject.com/ticket/34753#comment:7>

Django

unread,
Dec 20, 2025, 2:05:37 AM12/20/25
to django-...@googlegroups.com
#34753: Document how to safely construct email addresses
-------------------------------------+-------------------------------------
Reporter: Sylvain Fankhauser | Owner: Raghav
Type: | Bodani
Cleanup/optimization | Status: assigned
Component: Documentation | Version:
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Raghav Bodani):

* has_patch: 0 => 1

--
Ticket URL: <https://code.djangoproject.com/ticket/34753#comment:8>

Django

unread,
Feb 9, 2026, 6:26:12 PMFeb 9
to django-...@googlegroups.com
#34753: Document how to safely construct email addresses
-------------------------------------+-------------------------------------
Reporter: Sylvain Fankhauser | Owner: Raghav
Type: | Bodani
Cleanup/optimization | Status: assigned
Component: Documentation | Version:
Severity: Normal | Resolution:
Keywords: email | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 1
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Mike Edmunds):

* keywords: => email
* needs_better_patch: 0 => 1

--
Ticket URL: <https://code.djangoproject.com/ticket/34753#comment:9>

Django

unread,
Jun 4, 2026, 1:52:43 PM (2 days ago) Jun 4
to django-...@googlegroups.com
#34753: Document how to safely construct email addresses
--------------------------------------+------------------------------------
Reporter: Sylvain Fankhauser | Owner: (none)
Type: Cleanup/optimization | Status: new
Component: Documentation | Version:
Severity: Normal | Resolution:
Keywords: email | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 1
Easy pickings: 0 | UI/UX: 0
--------------------------------------+------------------------------------
Changes (by Jacob Walls):

* owner: Raghav Bodani => (none)
* status: assigned => new

--
Ticket URL: <https://code.djangoproject.com/ticket/34753#comment:10>

Django

unread,
Jun 4, 2026, 2:10:44 PM (2 days ago) Jun 4
to django-...@googlegroups.com
#34753: Document how to safely construct email addresses
-------------------------------------+-------------------------------------
Reporter: Sylvain Fankhauser | Owner: Mike
Type: | Edmunds
Cleanup/optimization | Status: assigned
Component: Documentation | Version:
Severity: Normal | Resolution:
Keywords: email | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 1
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Mike Edmunds):

* owner: (none) => Mike Edmunds
* status: new => assigned

Comment:

Copying in some of my notes (from a comment in the earlier PR):

I think we need to substantially rework the whole text in the section, so
it emphasizes the part that's the *developer's* responsibility (preventing
email address syntax injection). I'm not sure how best to word it, but
here's what I'd want to convey and roughly the order:

* Email header injection is a security exploit in which an attacker
manipulates email headers to change the intended sender, recipients, other
headers, or even the entire message.
* Header injection can be caused by newlines in header values (CRLF
injection) or by certain characters like commas and parentheses in address
headers (address syntax injection). [There doesn't seem to be a standard
term for the second type. We could instead say "header content injection"
or "delimiter injection".]
* Django builds on Python's email library, which prevents CRLF injection.
You'll get a `ValueError` if you try to send a message with newlines in
header values.
* But Django can't protect you from address syntax injection. **You are
responsible for properly sanitizing email addresses** constructed from
user-supplied or variable data.
* The best way to construct email addresses is using a well-tested library
function intended for the purpose, like Python's
`email.headerregistry.Address` object or (if you don't need IDN support)
the legacy `email.utils.formataddr()` function. (See example below.)
* **Never** try to use string formatting like `f'"{name}" <{email}>'` to
compose an email address header. This is unsafe, just like building SQL or
HTML with string formatting.
* If you're sending email that includes user-supplied content, you'll
likely need to take steps to prevent spoofing, spear-phishing, spam
cannons, and other malicious uses of email. (Details are beyond the scope
of Django docs.)
* Here's an example contact form that demonstrates some of these concepts…

Also, I've been trying to find a replacement link for
http://www.nyphp.org/phundamentals/8_Preventing-Email-Header-
Injection.html, which is heavily PHP centric and a bit outdated. All the
general descriptions I could find cover only CRLF injection, not address
syntax injection. (And they all use similar PHP code examples.) But maybe
one of these? Most are commercial sites; don't know if we have a policy
about that:
* https://beaglesecurity.com/blog/vulnerability/email-header-
injection.html
* https://www.invicti.com/learn/email-injection
* https://www.acunetix.com/blog/articles/email-header-injection/ (same
content, related company, different formatting)
* https://en.wikipedia.org/wiki/Email_injection (stub article)
--
Ticket URL: <https://code.djangoproject.com/ticket/34753#comment:11>
Reply all
Reply to author
Forward
0 new messages