GmailのSMTPサーバーにコマンドを送ってメールを送信する

mail

SMTP (Simple Mail Transfer Protocol)

SMTPはメールクライアントからサーバーへ、あるいはサーバー間でメールを送信する際のプロトコル。 受信する際のプロトコルとしては、認証とメールのダウンロード・削除を行うPOP (Post Office Protocol) や、 複数の環境から見ることを想定した、サーバーのメールボックスを管理しアクセスするIMAP (Internet Message Access Protocol) がある。

SMTP標準には認証の仕組みが含まれておらず、以前はPOPの認証が用いられていたが、現在はSMTP-AUTHという拡張が標準化されて用いられている。 SMTPでは通常25番ポートを使うが、スパムメール対策としてプロバイダがOP25B(Outbound Port 25 Blocking)を行うようになったので、 メールクライアントからサーバーへの通信はSMTP-AUTHの認証がかかる587や465番ポートで行われる。これらのポートはサブミッションポートと呼ばれる。

通信はテキストで行われ、HELO example.comのようなコマンドを送ると250 smtp.gmail.com at your serviceのようなリプライコードとメッセージが返ってくる。

telnetでのメール送信(失敗)

telnetでGmailのSMTPサーバーにコマンドを送ってみる。

DNSのMXレコードで返ってきたアドレスに接続しようとしたが、これらはサーバー間のリレー専用のようでサブミッションポートが空いていなかった。 なので接続する先はドキュメントに書いてあるsmtp.gmail.com

$ dig gmail.com mx +short
10 alt1.gmail-smtp-in.l.google.com.
20 alt2.gmail-smtp-in.l.google.com.
30 alt3.gmail-smtp-in.l.google.com.
40 alt4.gmail-smtp-in.l.google.com.
5 gmail-smtp-in.l.google.com.

$ telnet alt1.gmail-smtp-in.l.google.com. 587
...
telnet: Unable to connect to remote host

接続が完了すると220(Service ready)とメッセージが送られてきた。 HELOコマンドでクライアントのドメイン名を送ると250(Requested mail action okay, completed)が返った。 続けてMAILコマンドで送信元のアドレスを送ったところSTARTTLSでTLS接続に切り替えることを要求されたので実行すると サーバーからレスポンスが返ってきた後、TLSで再接続できずにコネクションが切れてしまった。 telnetはTLSハンドシェイクをサポートしていないのでここまで。

$ telnet smtp.gmail.com 587
...
220 smtp.gmail.com ESMTP ****** - gsmtp
HELO example.com
250 smtp.gmail.com at your service
MAIL FROM: <*****@gmail.com>
530 5.7.0 Must issue a STARTTLS command first. ****** - gsmtp
STARTTLS
220 2.0.0 Ready to start TLS
MAIL FROM: <*****@gmail.com>
Connection closed by foreign host.

opensslでのメール送信

TLS接続できるopensslで再挑戦。今度は認証を要求されたので AUTH PLAIN base64("\0メールアドレス\0パスワード")を送ったところ、2FAが有効なためにアプリパスワードでないといけないようなので発行した。 MAILRCPTコマンドで送信元と送信先を設定しDATAコマンドを送ると354が返ってくるのでヘッダーと本文を書いて最後に.を送るとメールが送信される。 ヘッダーと本文の間は一行空け、ヘッダーに日本語のような非ASCII文字を含める場合は=?UTF-8?B?<UTF-8をbase64エンコードしたもの>?=のようなフォーマットにする必要がある。

$ echo -ne '\00*****@gmail.com\00password' | base64
$ openssl s_client -connect smtp.gmail.com:465 -crlf -ign_eof
...
220 smtp.gmail.com ESMTP ****** - gsmtp
HELO example.com
250 smtp.gmail.com at your service
MAIL FROM: <*****@gmail.com>
530-5.7.0 Authentication Required. Learn more at
530 5.7.0  https://support.google.com/mail/?p=WantAuthError ****** - gsmtp
AUTH PLAIN *****
534-5.7.9 Application-specific password required. Learn more at
534 5.7.9  https://support.google.com/mail/?p=InvalidSecondFactor ****** - gsmtp
AUTH PLAIN *****
235 2.7.0 Accepted
MAIL FROM: <*****@gmail.com>  
250 2.1.0 OK ****** - gsmtp
RCPT TO: <*****@gmail.com>
250 2.1.5 OK ****** - gsmtp
DATA
354  Go ahead ****** - gsmtp
Subject: testmail

this mail is test
.
250 2.0.0 OK  ****** ****** - gsmtp
quit
221 2.0.0 closing connection ****** - gsmtp
read:errno=0

送信されたメールを見るとFromにはアドレスが表示されているが、Toには何も入っていない。これは今回含めていないFromとToヘッダーの値のようで、 FromヘッダーはGmailによって認証に用いたアカウントのものに上書きされるようだ。Fromヘッダーを含めた場合、元々の値はX-Google-Original-Fromとして送られていた。 また、返信先であるReturn-PathMAIL FROMの値になっていなかった。

送信されたメール

ということでGmailのSMTPサーバーで送信する場合、誰かになりすますことはできなさそうだが、そうでなくてもTXTレコードのv=spf1 include:_spf.google.com ~allのような文字列で送信サーバーが正しいか確認するSPF (Sender Policy Framework)やヘッダーに署名を含めるDKIM (DomainKeys Identified Mail)といった手段で対策されている。

参考

Simple Mail Transfer Protocol - Wikipedia

POP3(Post Office Protocol version 3):インターネット・プロトコル詳説(7) - @IT

StartTLSとは? | SendGridブログ

How to send email using simple SMTP commands via Gmail? - Stack Overflow

日本語メールの仕組み | SendGridブログ