After Upgrading to Django 5 and Updating Dependencies, SSL: CERTIFICATE_VERIFY_FAILED Error Occurs When Sending Emails

Django
2024-04-15 17:35 (8 months ago) ytyng

Issue Description

After upgrading Django to version 5.0 and updating other libraries, an error occurred when executing the following email sending code:

message = EmailMessage(
    subject='Verification Email',
    body='This is a verification email. Please ignore it.',
    from_email='developer@example.com',
    to=['test-user@example.com'],
    reply_to=['developer@example.com'],
)
send_result = message.send()

The following error message was displayed:

Traceback (most recent call last):
  File "<...>python3.11/site-packages/django/core/management/base.py", line 413, in run_from_argv
    self.execute(*args, **cmd_options)
  File "<...>python3.11/site-packages/django/core/management/base.py", line 459, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<...>management/commands/send_test_mail.py", line 34, in handle
    send_result = message.send()
                  ^^^^^^^^^^^^^^
  File "<...>python3.11/site-packages/django/core/mail/message.py", line 300, in send
    return self.get_connection(fail_silently).send_messages([self])
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<...>python3.11/site-packages/django/core/mail/backends/smtp.py", line 128, in send_messages
    new_conn_created = self.open()
                       ^^^^^^^^^^^
  File "<...>python3.11/site-packages/django/core/mail/backends/smtp.py", line 93, in open
    self.connection.starttls(context=self.ssl_context)
  File "<...>lib/python3.11/smtplib.py", line 790, in starttls
    self.sock = context.wrap_socket(self.sock,
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<...>lib/python3.11/ssl.py", line 517, in wrap_socket
    return self.sslsocket_class._create(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<...>lib/python3.11/ssl.py", line 1108, in _create
    self.do_handshake()
  File "<...>lib/python3.11/ssl.py", line 1379, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate (_ssl.c:1006)

The SMTP server being connected to is one created by installing Postfix on an EC2 instance.

It seems that this error occurs because the self-signed certificate cannot be verified.

Email Sending Using smtplib

Incidentally, when using the same server with smtplib:

s = smtplib.SMTP(...)
s.starttls()
s.ehlo()
s.login(...)
s.sendmail(...)

The above error does not occur.

Proposed Solutions

As a temporary measure, by modifying the following part of the email sending code:

self.connection.starttls(context=self.ssl_context)

If you pass ssl._create_unverified_context() to context, it will work. (By default, ssl.create_default_context() is used.)

However, skipping certificate verification is not a good practice.

Emergency Measures

For instance, creating a function like the one below and using it in place of EmailMessage.send will allow sending the email while skipping the verification and issuing a warning:

def send_message_use_unverified_backend(email_message, *, fail_silently=False):
    if not email_message.recipients():
        return 0
    connection = email_message.get_connection(fail_silently=fail_silently)
    connection.ssl_context = ssl._create_unverified_context()
    return connection.send_messages([email_message])

message = EmailMessage(...)

send_message_use_unverified_backend(message)

Ideally, instead of the above workaround, it's better to use managed SMTP services like Amazon SES or Sendgrid.

Currently unrated

Comments

Archive

2024
2023
2022
2021
2020
2019
2018
2017
2016
2015
2014
2013
2012
2011