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

36 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 PMJun 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 PMJun 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>

Django

unread,
Jun 12, 2026, 5:10:12 PMJun 12
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: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Mike Edmunds):

* needs_better_patch: 1 => 0

Comment:

https://github.com/django/django/pull/21467
--
Ticket URL: <https://code.djangoproject.com/ticket/34753#comment:12>

Django

unread,
Jun 12, 2026, 5:13:47 PMJun 12
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: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Mike Edmunds):

I ended up removing the contact-form-like example entirely. It ended up
not helpful. (And we have a better contact form example in the forms docs,
though that has some of the issues in my earlier comment. See #37162.)
--
Ticket URL: <https://code.djangoproject.com/ticket/34753#comment:13>

Django

unread,
Jun 29, 2026, 5:02:59 PM (3 days ago) Jun 29
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: dev
Severity: Normal | Resolution:
Keywords: email | Triage Stage: Ready for
| checkin
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Natalia Bidart):

* stage: Accepted => Ready for checkin
* version: => dev

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

Django

unread,
Jun 30, 2026, 1:20:23 PM (2 days ago) Jun 30
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: dev
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):

* needs_better_patch: 0 => 1
* stage: Ready for checkin => Accepted

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

Django

unread,
Jun 30, 2026, 3:07:25 PM (2 days ago) Jun 30
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: dev
Severity: Normal | Resolution:
Keywords: email | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Mike Edmunds):

* needs_better_patch: 1 => 0

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

Django

unread,
Jul 1, 2026, 9:50:21 PM (7 hours ago) Jul 1
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: dev
Severity: Normal | Resolution:
Keywords: email | Triage Stage: Ready for
| checkin
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Natalia Bidart):

* stage: Accepted => Ready for checkin

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

Django

unread,
Jul 1, 2026, 9:57:16 PM (7 hours ago) Jul 1
to django-...@googlegroups.com
#34753: Document how to safely construct email addresses
-------------------------------------+-------------------------------------
Reporter: Sylvain Fankhauser | Owner: Mike
Type: | Edmunds
Cleanup/optimization | Status: closed
Component: Documentation | Version: dev
Severity: Normal | Resolution: fixed
Keywords: email | Triage Stage: Ready for
| checkin
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by nessita <124304+nessita@…>):

* resolution: => fixed
* status: assigned => closed

Comment:

In [changeset:"f3f9601cff03b38942e70ce8042bdfdec6002449" f3f9601c]:
{{{#!CommitTicketReference repository=""
revision="f3f9601cff03b38942e70ce8042bdfdec6002449"
Fixed #34753 -- Extended security and safety remarks in email topics docs.

Reworked the outdated "Preventing header injection" section:

* Added a "Safely sending email" section noting that the topic is
relevant but beyond the scope of Django's own docs.
* Added a section on correctly formatting email addresses with a
variable display name to avoid injection attacks.
* Updated the existing "Preventing header injection" section to note
that Django (via Python) already prevents CRLF injection, but that
custom email backends that bypass Django's protections may need to
handle it.

Also added references to the new "Formatting email addresses" section
from the `ADMINS`, `DEFAULT_FROM_EMAIL`, and `SERVER_EMAIL` settings.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/34753#comment:18>

Django

unread,
Jul 1, 2026, 10:03:45 PM (7 hours ago) Jul 1
to django-...@googlegroups.com
#34753: Document how to safely construct email addresses
-------------------------------------+-------------------------------------
Reporter: Sylvain Fankhauser | Owner: Mike
Type: | Edmunds
Cleanup/optimization | Status: closed
Component: Documentation | Version: dev
Severity: Normal | Resolution: fixed
Keywords: email | Triage Stage: Ready for
| checkin
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Natalia <124304+nessita@…>):

In [changeset:"50ea55323c79108413a5bc4052c9c059f2eb15fd" 50ea553]:
{{{#!CommitTicketReference repository=""
revision="50ea55323c79108413a5bc4052c9c059f2eb15fd"
[6.1.x] Fixed #34753 -- Extended security and safety remarks in email
topics docs.

Reworked the outdated "Preventing header injection" section:

* Added a "Safely sending email" section noting that the topic is
relevant but beyond the scope of Django's own docs.
* Added a section on correctly formatting email addresses with a
variable display name to avoid injection attacks.
* Updated the existing "Preventing header injection" section to note
that Django (via Python) already prevents CRLF injection, but that
custom email backends that bypass Django's protections may need to
handle it.

Also added references to the new "Formatting email addresses" section
from the `ADMINS`, `DEFAULT_FROM_EMAIL`, and `SERVER_EMAIL` settings.

Backport of f3f9601cff03b38942e70ce8042bdfdec6002449 from main.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/34753#comment:19>

Django

unread,
Jul 1, 2026, 10:06:15 PM (7 hours ago) Jul 1
to django-...@googlegroups.com
#34753: Document how to safely construct email addresses
-------------------------------------+-------------------------------------
Reporter: Sylvain Fankhauser | Owner: Mike
Type: | Edmunds
Cleanup/optimization | Status: closed
Component: Documentation | Version: dev
Severity: Normal | Resolution: fixed
Keywords: email | Triage Stage: Ready for
| checkin
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Natalia Bidart):

I won't backport these changes to `stable/6.0.x` because
`docs/topics/email.txt` is considerably different following the `MAILERS`
work.
--
Ticket URL: <https://code.djangoproject.com/ticket/34753#comment:20>
Reply all
Reply to author
Forward
0 new messages