コンテンツにスキップ

Top

Python で メール送信

Python で メールを送信するプログラムを作成する。

メールの送信にはSMTPサーバが必要となるが、自分が契約しているメールサーバやGmailなどを用いれば試験できる。

その際、当然だが、SMTPサーバ側の設定に合わせないと送信できない。

昔、SMTPサーバは最初は何の認証もない25番ポートによる送信だった。

受信側のPOPサーバには認証はあった。あくまで送信する際に認証がなかったのだ。

理由は現実と置き換えて考えれば容易で、はがきを出すときはそこら辺のポストに適当にいれればよいけど、はがきが届くときは(基本的にはカギのかかった)郵便受けに入れられる。

それと同じでいちいちポストにカギなんか書けるわけがない、という発想からだ。

本来であればそれで問題ないのだが、世の中には悪い人間がいて、迷惑メールを大量に送信するのに他人のSMTPサーバを用いるケースが出てきた。
(これを一般的に「踏み台」にされるという)

これはいうなればポストに広告や詐欺はがきを大量に詰め込むようなもので、はがき代がかかる現実ではこんなことは起きない、もしくは起きたところでお金もらってるから特に問題ないのであった。
が、ネットでは無料なので大量の詐欺メールとかを他人のSMTPサーバから送り付ける悪人がでて来たのだった。

踏み台になっているメールサーバは当然批判されてしまうため対策を講じる必要がある。そうして生まれたのがSMTP AUTH、ようはSMTP側でもPOP側同様、認証をするよ!となったのである。

さらに、今まであえて「はがき」で例えてきたが、見られたくない内容もあるはずである。そのため「封書」に該当する暗号化がされるようになった。

それがSMTP over SSLである。しかしながこれは相手先にPOP/IMAPサーバが対応していないと受け取ってもらえない。そのため、相手が対応してないなら平文で送るSTARTTLSという方式も生まれた。

なお、SMTP認証は万能ではなく、内部のアカウントのパスワードが盗まれたら意味がないのでしっかり管理する必要がある。

主な送信方式

主な送信方式として以下がある。

方式 ポート番号 概要
SMTP 25 暗号化なし/認証なし
SMTP over SSL 465 暗号化/認証
STARTTLS 587 暗号化/認証

昔からある25番ポートによる送信は許可されていないのがほとんどで、OP25B(Outbound Port 25 Blocking)という25番ポートへのアクセスをそもそも拒否しているサーバがほとんどである。

よって2021年現在において25番ポートでメールを送信するのは現実的ではない。

SMTP over SSL か STARTTLS によって送るのがほとんどである。

SMTP over SSL と STARTTLS の主な違い

SMTP over SSL も STARTTLS も暗号化して認証もしている。ならどっちも同じだが何が違うのか?

シンプルに言えば相手側受信サーバが対応していない場合にどのように振る舞うか?である。

SMTP over SSL は相手のサーバがこの方式に対応していなかったら送信エラーになって終わりである。

しかしながら、STARTTLS の場合は相手のサーバが対応していなかったら平文で送ることによってより確実に届く可能性がでてくるのである。

ただし、そのため、STARTTLSは最初にIPアドレスなどを平文で相手に渡している。(相手が対応していればその後暗号化する)

この最初の部分が平文なのが嫌だ!今時SMTP over SSLに対応していないPOP/IMAPサーバなんてない!というのであればSMTP over SSLを選択するのもよいと思われる。

POP before SMTPってなに?

メールを送るときにPOP before SMTPというのを聞いたことがあるかもしれない。

これはPOPの認証機能を利用する送信手段だ。

まずメールを送信したい場合、POPサーバでの認証データをSMTPサーバに送る。

そしてPOPサーバで認証がOKであれば、一定期間の間、その機器(IPアドレスで特定)はメールを送信してよい!という機能である。

しかしながらその一定期間(10分など)がセキュリティホールになる場合もあるので、現在ではほぼ使われていない。

SMTP over SSL の送信サンプルコード

#!/usr/bin/env python3

import smtplib
import ssl

from email import encoders
from email import message
from email.mime import multipart
from email.mime import text
from email.mime.base import MIMEBase

# SMTPサーバ情報
smtp_server_host = ''   # SMTPサーバ
smtp_server_port = 465  # SMTPポート

# アカウント情報
account   = '' # SMTPアカウント
password  = '' # SMTPパスワード
from_mail = '' # 送信者メールアドレス

# メール
msg = multipart.MIMEMultipart()
msg['Subject'] = 'Mail Test' # Subject 
msg['From'] = ''             # from mail address
msg['To'] = ''               # to mail address(comma separated)
#msg['Cc'] = 'CC'
#msg['Bcc'] = 'BCC'

# メール本文 
msg.attach(text.MIMEText('Hello!', 'plain', 'utf-8'))

# 添付ファイル(画像)
attachment = MIMEBase('image', 'jpeg;name="attach.jpg"')

fp = open('./attach.jpg','rb')
attachment.set_payload(fp.read())
fp.close()

encoders.encode_base64(attachment)
attachment.add_header("Content-Dispositon","attachment",filename='attach.jpg')
msg.attach(attachment)

# 送信(SMTP over SSL)
context = ssl.create_default_context()
server = smtplib.SMTP_SSL(smtp_server_host, smtp_server_port, timeout=10, context=context)
server.login(account, password)
server.send_message(msg)
server.close()

SMTPサーバとかアカウントとか正しく書けばattach.jpgとともにメールを送信してくれる。
メールの文字は\r\nで改行できる。

STARTTLS の送信サンプルコード

STARTTLSの場合は少しコードを変えないといけない。

#!/usr/bin/env python3

import smtplib
import ssl

from email import encoders
from email import message
from email.mime import multipart
from email.mime import text
from email.mime.base import MIMEBase

# SMTPサーバ情報
smtp_server_host = ''  # SMTPサーバ
smtp_server_port = 587 # SMTPポート

# アカウント情報
account   = ''  # SMTPアカウント
password  = ''  # SMTPパスワード
from_mail = ''  # 送信者メールアドレス

# メール
msg = multipart.MIMEMultipart()
msg['Subject'] = 'Mail Test' # Subject 
msg['From'] = ''             # from mail address
msg['To'] = ''               # to mail address(comma separated)
#msg['Cc'] = 'CC'
#msg['Bcc'] = 'BCC'

# メール本文 
msg.attach(text.MIMEText('Test Mail', 'plain', 'utf-8'))

# 添付ファイル(画像)
attachment = MIMEBase('image', 'jpeg;name="attach.jpg"')

fp = open('./attach.jpg','rb')
attachment.set_payload(fp.read())
fp.close()

encoders.encode_base64(attachment)
attachment.add_header("Content-Dispositon","attachment",filename='attach.jpg')
msg.attach(attachment)

# 送信(STARTTLS)
server = smtplib.SMTP(smtp_server_host, smtp_server_port)
server.ehlo()
server.starttls()
server.ehlo()
server.login(account, password)
server.send_message(msg)
server.quit()

Gmailで Username and Password not accepted エラーが発生

Gmail のアカウントを使って送信したところ、

smtplib.SMTPAuthenticationError: (535, b'5.7.8 Username and Password not accepted. Learn more at\n5.7.8 https://support.google.com/mail/?p=BadCredentials')

というエラーが。

ユーザー名とかパスワードとか書いてるんで間違ったかな?と見直していたが違った。Gmailのセキュリティ設定の問題だった。

送信に使っているアカウントでGmailにログインし、設定画面の安全性の低いアプリのアクセスをONにしないと失敗するとのこと。

ので設定したら問題なく送信された!

ただし、Gmailからセキュリティに問題あるよ!アカウント消すよ、的なメールがくる。大丈夫だろうか?テストが終わったら戻しておこう。

以上!