Opened 2 years ago

Last modified 10 days ago

#34753 new Cleanup/optimization

Document how to safely construct email addresses

Reported by: Sylvain Fankhauser Owned by: nobody
Component: Documentation Version:
Severity: Normal Keywords:
Cc: Mike Edmunds Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

The documentation about sending email (https://docs.djangoproject.com/en/dev/topics/email/) only provides examples with recipients being only e-mail addresses, without the recipient name. I believe adding the name of the recipient to the To header is a standard practice, and I think Django could provide some guidance on how to escape it properly since it can easily be misused.

For example, a naive way of doing it would be to use f"{first_name} {last_name} <{email}>" (which will fail if first_name, last_name or email contain special characters such as <, >, " or ,. I’m actually guilty of using this in the past, only to find out at my own expense that this wasn’t a good idea). Another way would be to pass the result of sanitize_address((f"{first_name} {last_name}", email), "utf-8") to the to argument, which would work until someone has a name that’s long enough for sanitize_address to add a \n character in the middle, resulting in an error when sanitize_address will be called a second time when actually sending the mail.

I’m still not entirely sure of the proper way to do it properly (and I’m actually surprised I couldn’t find anything about this online). I think the proper way to do it would be to pass the result of email.utils.formataddr((f"{first_name} {last_name}", email)) to the to argument. If you think that’s the correct way to do it and you think the docs could be improved by adding a note about this, I can take care of submitting a patch.

Change History (4)

comment:1 by Mariusz Felisiak, 2 years ago

Resolution: invalid
Status: newclosed

Thanks for the ticket, however it's rather a support question. Django is not a mail server and we cannot document all related caveats, best practices, and how-to's.

Closing per TicketClosingReasons/UseSupportChannels.

comment:2 by Claude Paroz, 2 years ago

I would not be so categorical, I think that this is a common use case and a note in the docs wouldn't hurt. Maybe the note would simply redirect to an external reference (Python docs or RFC).

comment:3 by Jacob Walls, 10 days ago

Cc: Mike Edmunds added
Resolution: invalid
Status: closednew
Summary: Document how to properly escape `to` in email messagesDocument how to safely construct email addresses
Triage Stage: UnreviewedAccepted
Type: UncategorizedCleanup/optimization
Version: 4.2

Reopening and accepting per forum discussion, see Mike's implementation advice there.

comment:4 by Mike Edmunds, 10 days ago

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-form@example.com> (but formatted safely)
  • to would be a constant (["contact@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. But people building contact forms often think that's how to do it.)

Last edited 10 days ago by Mike Edmunds (previous) (diff)
Note: See TracTickets for help on using tickets.
Back to Top