[Django] #32008: django.core.mail.message.sanitize_address can add newlines in a header that django.core.mail.EmailMessage will refuse

37 views
Skip to first unread message

Django

unread,
Sep 15, 2020, 7:47:55 AM9/15/20
to django-...@googlegroups.com
#32008: django.core.mail.message.sanitize_address can add newlines in a header that
django.core.mail.EmailMessage will refuse
------------------------------------------------+------------------------
Reporter: Pierre-Elliott Bécue | Owner: nobody
Type: Bug | Status: new
Component: Core (Mail) | Version: 2.2
Severity: Normal | Keywords: mail
Triage Stage: Unreviewed | Has patch: 0
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
------------------------------------------------+------------------------
Hi,

We've come accross a situation with django 2.2 where, while sanitazing a
user address to send a mail in his name, the sanitize_address function,
which relies on python's email.header.Header will introduce a newline
character in the from header, and therefore, the mail won't get send
because django's security features include refusing emails with newlines
in headers. It seems to me that no recent version of django addresses this
issue.

A simple solution would be to have sanitize_address take a maxlinelen
parameter passed to Header. A more complex solution would be to see if the
newline is followed by spaces or tabulations, in which case it doesn't
seem to pose a security risk as it can't lead to an embedded header.

If you need more input I can give som.

--
Ticket URL: <https://code.djangoproject.com/ticket/32008>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Sep 15, 2020, 8:10:22 AM9/15/20
to django-...@googlegroups.com
#32008: sanitize_address() can add newlines in a header that
django.core.mail.EmailMessage will refuse.
-------------------------------------+-------------------------------------
Reporter: Pierre-Elliott | Owner: nobody
Bécue |
Type: Bug | Status: closed

Component: Core (Mail) | Version: 2.2
Severity: Normal | Resolution: needsinfo

Keywords: mail | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by felixxm):

* status: new => closed
* resolution: => needsinfo


Comment:

Can you provide more details e.g. use case or a sample project?
`sanitize_address()` is a part of internal and undocumented API which
works fine in Django itself.

--
Ticket URL: <https://code.djangoproject.com/ticket/32008#comment:1>

Django

unread,
Sep 15, 2020, 8:40:16 AM9/15/20
to django-...@googlegroups.com
#32008: sanitize_address() can add newlines in a header that
django.core.mail.EmailMessage will refuse.
-------------------------------------+-------------------------------------
Reporter: Pierre-Elliott | Owner: nobody
Bécue |
Type: Bug | Status: closed
Component: Core (Mail) | Version: 2.2
Severity: Normal | Resolution: needsinfo

Keywords: mail | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Pierre-Elliott Bécue):

Hi,


{{{
In [1]: from django.core.mail.message import sanitize_address

In [2]: from django.core.mail import EmailMessage

In [3]: msg = EmailMessage(from_email=sanitize_address("Pierre-Elliott
Charles Maxime Antoine Bécue via nm.debian.org <p...@debian.org>",
'utf-8'), subject="Test", body="", to
...: =[sanitize_address("Pierre-Elliott Charles Maxime Antoine Bécue
via nm.debian.org <p...@debian.org>", 'utf-8'),])

In [4]: msg.send()
---------------------------------------------------------------------------
BadHeaderError Traceback (most recent call
last)
<ipython-input-13-e80ddd8be11c> in <module>()

----> 1 msg.send()
/usr/lib/python3/dist-packages/django/core/mail/message.py in send(self,
fail_silently)
304 # send to.
305 return 0
--> 306 return
self.get_connection(fail_silently).send_messages([self])
307
308 def attach(self, filename=None, content=None, mimetype=None):

/usr/lib/python3/dist-packages/django/core/mail/backends/smtp.py in
send_messages(self, email_messages)
108 num_sent = 0
109 for message in email_messages:
--> 110 sent = self._send(message)
111 if sent:
112 num_sent += 1

/usr/lib/python3/dist-packages/django/core/mail/backends/smtp.py in
_send(self, email_message)
122 from_email = sanitize_address(email_message.from_email,
encoding)
123 recipients = [sanitize_address(addr, encoding) for addr in
email_message.recipients()]
--> 124 message = email_message.message()
125 try:
126 self.connection.sendmail(from_email, recipients,
message.as_bytes(linesep='\r\n'))

/usr/lib/python3/dist-packages/django/core/mail/message.py in
message(self)
269 msg = self._create_message(msg)
270 msg['Subject'] = self.subject
--> 271 msg['From'] = self.extra_headers.get('From',
self.from_email)
272 self._set_list_header_if_not_empty(msg, 'To', self.to)
273 self._set_list_header_if_not_empty(msg, 'Cc', self.cc)

/usr/lib/python3/dist-packages/django/core/mail/message.py in
__setitem__(self, name, val)
182
183 def __setitem__(self, name, val):
--> 184 name, val = forbid_multi_line_headers(name, val,
self.encoding)
185 MIMEText.__setitem__(self, name, val)
186

/usr/lib/python3/dist-packages/django/core/mail/message.py in
forbid_multi_line_headers(name, val, encoding)
60 val = str(val) # val may be lazy
61 if '\n' in val or '\r' in val:
---> 62 raise BadHeaderError("Header values can't contain newlines
(got %r for header %r)" % (val, name))
63 try:
64 val.encode('ascii')

BadHeaderError: Header values can't contain newlines (got '=?utf-8?q
?Pierre-Elliott_Charles_Maxime_Antoine_B=C3=A9cue_via_nm=2Edebian?=\n
=?utf-8?q?=2Eorg?= <p...@debian.org>'
for header 'From')
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/32008#comment:2>

Django

unread,
Sep 15, 2020, 8:57:25 AM9/15/20
to django-...@googlegroups.com
#32008: sanitize_address() can add newlines in a header that
django.core.mail.EmailMessage will refuse.
-------------------------------------+-------------------------------------
Reporter: Pierre-Elliott | Owner: nobody
Bécue |
Type: Bug | Status: new
Component: Core (Mail) | Version: 2.2
Severity: Normal | Resolution:

Keywords: mail | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Pierre-Elliott Bécue):

* status: closed => new
* resolution: needsinfo =>


Comment:

I don't now if the closing was voluntary and why the bug got closed, but
as you asked me more info I guess I should reopen it...

--
Ticket URL: <https://code.djangoproject.com/ticket/32008#comment:3>

Django

unread,
Sep 15, 2020, 11:12:23 AM9/15/20
to django-...@googlegroups.com
#32008: sanitize_address() can add newlines in a header that
django.core.mail.EmailMessage will refuse.
-------------------------------------+-------------------------------------
Reporter: Pierre-Elliott | Owner: nobody
Bécue |
Type: Bug | Status: closed

Component: Core (Mail) | Version: 2.2
Severity: Normal | Resolution: invalid

Keywords: mail | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by felixxm):

* status: new => closed

* resolution: => invalid


Comment:

`forbid_multi_line_headers()` calls `sanitize_address()` so you don't need
to do this explicitly. Moreover `sanitize_address()` is a part of internal
and undocumented API, it's not intended for preparing values for
`EmailMessage()`.

--
Ticket URL: <https://code.djangoproject.com/ticket/32008#comment:4>

Django

unread,
Sep 15, 2020, 11:28:17 AM9/15/20
to django-...@googlegroups.com
#32008: sanitize_address() can add newlines in a header that
django.core.mail.EmailMessage will refuse.
-------------------------------------+-------------------------------------
Reporter: Pierre-Elliott | Owner: nobody
Bécue |
Type: Bug | Status: closed
Component: Core (Mail) | Version: 2.2
Severity: Normal | Resolution: invalid

Keywords: mail | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Pierre-Elliott Bécue):

Thanks, what's the appropriate entry point to sanitize headers before
passing them to EmailMessage then ?

--
Ticket URL: <https://code.djangoproject.com/ticket/32008#comment:5>

Django

unread,
Sep 16, 2020, 3:06:28 AM9/16/20
to django-...@googlegroups.com
#32008: sanitize_address() can add newlines in a header that
django.core.mail.EmailMessage will refuse.
-------------------------------------+-------------------------------------
Reporter: Pierre-Elliott | Owner: nobody
Bécue |
Type: Bug | Status: closed
Component: Core (Mail) | Version: 2.2
Severity: Normal | Resolution: invalid

Keywords: mail | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Claude Paroz):

I think `EmailMessage` is doing sanitizing itself, did you get errors when
using your "raw" From address?

--
Ticket URL: <https://code.djangoproject.com/ticket/32008#comment:6>

Reply all
Reply to author
Forward
0 new messages