Opened 2 years ago

Last modified 2 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, 2 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, 2 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 2 days ago by Mike Edmunds (previous) (diff)
Note: See TracTickets for help on using tickets.
Back to Top