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.
* 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>
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>
* 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>
* 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>
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>
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>