Django を 5.0 にアップデートし、他のライブラリも更新した所、下記のメール送信のコードを実行した所
message = EmailMessage(
subject='検証用メール',
body='検証用のメールを送信しています。無視してください。',
from_email='developer@example.com',
to=['test-user@example.com'],
reply_to=['developer@example.com'],
)
send_result = message.send()
下記のようなエラーメッセージが出た。
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)
接続先のSMTPサーバーは、EC2 に Postfix をインストールして作ったもの。
自己署名証明書がベリファイできないとこのエラーが出るようになってしまったようだ。
ちなみに、同サーバーを使う際、 smtplib を使って
s = smtplib.SMTP(...)
s.starttls()
s.ehlo()
s.login(...)
s.sendmail(...)
とした場合、上記エラーは出なかった。
とりいそぎの対応として、メール送信箇所のコードの下記箇所について
self.connection.starttls(context=self.ssl_context)
context
に、 ssl._create_unverified_context()
を入れれば動く。(本来は ssl.create_default_context()
を入れている)
証明書のベリファイをスキップするので良い方法ではない。
例えば、下記のような関数を作り、EmailMessage.send
の代わりに使えば、警告とベリファイをキャンセルして送信ができる。
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)
本来は、上記対応よりも Amazon SES とか Sendgrid 等のマネージドな SMTP を使ったほうが良い。
コメント