Yahoo! Mailの送信制限回避法について
Yahoo! Mailでは2020年4月頃より一定期間(およそ二週間)POPアクセス(メールボックスからのメールの取得)がないアカウントはメールソフトからは送信できなくなる制限が設けられました。本稿ではその制限を回避するためLinux上で機械的で定期的なPOPアクセスを実現する方法を考察します。
2021-09-11
1. はじめに
2. 実証環境
3. POPアクセス
4. SSL(暗号化)
5. 実践例
6. まとめ
Yahoo! Mailでは2020年4月頃より一定期間(およそ二週間)POPアクセス(メールボックスからのメールの取得)がないアカウントはメールソフトからは送信できなくなる制限(「Yahoo! JAPAN公式サービスを利用したアクセスのみ有効にする」に自動的に設定変更されてしまう)が設けられました。この制限によりYahoo! Mailを送信サーバとしてのみ利用していたユーザは不必要なPOPアクセスを強いられることになりました(なお、この制限にかかったとしてもアカウントの設定ページから再度利用可能にすることはできます)。
通常、POPアクセスはメールソフトから対話的に行われるためこれを非対話的に自動化するのは意外に厄介です。また、一般的なメールソフトでは定期的な自動取得もサポートしているのでそれに任すのも一案ですが、この問題を解決するためだけに不要なYahoo! Mailのスパムを他の通常のメールと同様に管理監視するのもスマートではありません。本稿では、この制限を回避するためLinux上で機械的で定期的なPOPアクセスを実現する方法を考察します。
次項で本稿での実証環境(Linux)について述べ、第3項で非対話的に実行可能なPOPアクセスを実現し、第4項ではそのPOPアクセスをSSLにより暗号化します。第5項で実践例を示し、最終第6項がまとめとなります。
実証環境は、Fedora 34およびVine Linux 6.5です。本稿の実証はbashスクリプトで行われるのでbash(4.x以上)が動作すればおそらく異なるプラットフォームでも再現可能と思われますが、検証は行っていません。
メールボックスからメールを取得するためのPOPは、ソケットで接続し、そこでテキストを読み書きするだけの単純なプロトコルで簡単なスクリプトで実装可能です。以下はそのスクリプトの概要です。
bashでソケット接続するには
exec 3<>/dev/tcp/pop_server/110
とします。これで、pop_serverのtcp 110ポートにソケットで接続できます。上記の「3」はファイルディスクリプタの番号で、3からシステムの最大値まで任意に指定できます。ソケット接続後は、書き込みは「>&3」に、読み出しは「<&3」から行います。つまり、POPのコマンドを「>&3」に書き込み、サーバからの返答を「<&3」で受け取る、という形で通信していきます。実際のところはメール取得のコマンドを発するだけで本稿の目的は達せられるのでサーバからの返答を逐一受け取る必要はないのですが、ここではサーバからの反応がないと不安になるので表示していきます。
read pop3_return <&3
echo ${pop3_return//$'\r'/}
ソケットが開かれるとPOPサーバはまず接続が確立したことを示すグリーティングを送ってきます。上記はそれを確認するためにreadで読み出し、それをechoで出力しています。Yahoo!のPOPサーバでは「+OK hello from popgate-2.1.153-1 popgate5009.mail.kks.ynwl.yahoo.co.jp」のようなグリーティングを返してきます。なお、POPで使われる改行は<CR><LF>であり、改行が<LF>のみのUnix環境では問題を起こしやすいので<CR>を省く処理をした上で出力しています。
接続が確立したのでUSERコマンドで自己のアカウントを名乗ります。コマンドの送出では<CR><LF>の改行を明示的に付加しています。問題がなければ「+OK password required.」のような返答が帰ってきます。POPでは基本的に行単位で返答が返されるのでreadで行を読んでいきます。
echo -ne "USER user_acount\r\n" >&3
read pop3_return <&3
echo ${pop3_return//$'\r'/}
続いてPASSコマンドでパスワードを入力します。
echo -ne "PASS pop_password\r\n" >&3
read pop3_return <&3
echo ${pop3_return//$'\r'/}
パスワードが通ると「+OK Maildrop ready, (JPOP server ready).」のような返答があり、RETRコマンドでメールの取得が可能になります。以下で1番に該当するメールを取得します。
echo -ne "RETR 1\r\n" >&3
POPサーバはRETRコマンドを受けるとまずRETRコマンドを承認したことを示す+OK、次に空白をはさんで本文の長さを付加し、改行した後、メールの本文を送出します。メールの本文はこれまでのようなコマンドへの返答とは異なり、通常、一行だけでは終了しません。メール本文の終了は"."だけの行の出現を待ちます。通常のファイルとは異なり、この場合の入力にはサーバ側からEOFが出力されないため入力の終了をクライアント側で決定する必要があります。そのトリガーが"."になります。実装的にはこれまで通り、readで行を読んでいき、最終行かどうかを評価していきます。
while read pop3_return ;do
pop3_return=${pop3_return//$'\r'/}
if [ "$pop3_return" = "." ];then
break
fi
if [ "$mail_body" ];then
mail_body="$mail_body
$pop3_return"
else
mail_body=$pop3_return
fi
done <&3
echo "$mail_body"
メール本文が終了したら、POPアクセスをQUITコマンドで終了します。
echo -ne "QUIT\r\n" >&3
read pop3_return <&3
echo ${pop3_return//$'\r'/}
POPサーバが「+OK Server signing off.」のような返答を返し、このセッションが終了します。単純には上記のようなスクリプトをcronなどで定期実行すれば、Yahoo! Mailの利用制限設定を自動変更されてしまう不具合を回避することができます。先述したようにPOPサーバからの返答は不要なのでスクリプトをより単純にしたければ適宜該当箇所を省略して下さい。
しかしながら、実際に上記のスクリプトをYahoo! Mailで実行しても動作しません。正直、今、気づきました。以前は110番ポートでも普通に接続できたのですが、どうやら2020年4月に行われたYahoo! Mailのシステム更新では利用制限の自動変更の他、暗号化されていないポートの閉鎖も実行されたようです。というわけで次項で暗号化します。
以前のYahoo! Mailでは暗号化されていない通信も普通にできたため、パスワードもメール本文も丸見えになっている人がけっこういたはずです。メールは葉書のようなもの、他人に見えても支障はない、というのも一つの見解ではありますが、Yahoo!の場合はアカウントそのもののパスワードとメール関連のパスワードも同一なためパスワードが流出したときの影響は小さくありません。さすがにこのご時世、暗号化に対応していないメールソフトはないだろう、あっても無視していいだろうということでYahoo!も暗号化を必須としたようです。そこでここでは前項のPOPアクセスの暗号化を考えます。
暗号化は、暗号化された通路を作り、その暗号化された通路を通して前項の通信を行うだけです。暗号化された通路はstunnelで開きます。通路の一方の端点をPOPサーバに接続し、もう一方の端点を自機の中に開き、そこに接続すれば暗号化された通路を通ってPOPサーバにたどり着けるという構成になります。自機の中の端点をかりにlocalhost(自機)の10005番ポートとすると
exec 3<>/dev/tcp/localhost/10005
としてソケットを開き、接続が確立すれば後は前項とまったく同様のことをするだけです。
stunnelはPOPクライアントがソケットを開くよりも前に実行します。でないと、当然、POPクライアントはソケットを開けません。stunnelの設定は基本的に設定ファイルで行います。
pid = /var/tmp/stunnel.pid
[pop3s-wrapper]
client = yes
accept = 10005
connect = pop.mail.yahoo.co.jp:995
上記のような設定ファイルを作成し、その設定ファイルが/tmp/stunnel.confとすると
stunnel /tmp/stunnel.conf
として実行します。設定ファイルのpid値はstunnelのPIDをどのファイルに書き込むかを絶対パスで指定します。stunnelは一度実行されるとデーモンとして動作し、スクリプトが終了しても動き続けます。本稿のような使い方ではスクリプトが終了すればstunnelも用済みになるのでこのPIDを頼りにkillするのがお行儀の良いやり方です。次の[pop3s-wrapper]はこの設定の個別の名称で任意の名称を[]で囲みます。client値は、この設定がサーバとして動作するのかクライアントとして動作するのかを指定するものです。デフォルトはサーバなので本稿のようにクライアントとして動作させる場合は「client = yes」は必須の設定です。accept値はlocalhostの待ち受けポートで、connect値は接続先です。暗号化されたPOPのポート番号は一般的に995でYahoo!でも同じです。
このlocalhostのポートからstunnelによって暗号化された通路を通してconnect値の接続先と通信するということになります。これまでの一連の流れをまとめると以下のようになります。
#!/bin/sh
stunnel /tmp/stunnel.conf 2>/dev/null
exec 3<>/dev/tcp/localhost/10005
read pop3_return <&3
echo ${pop3_return//$'\r'/}
echo -ne "USER user_acount\r\n" >&3
read pop3_return <&3
echo ${pop3_return//$'\r'/}
echo -ne "PASS pop_password\r\n" >&3
read pop3_return <&3
echo ${pop3_return//$'\r'/}
echo -ne "RETR 1\r\n" >&3
while read pop3_return ;do
pop3_return=${pop3_return//$'\r'/}
if [ "$pop3_return" = "." ];then
break
elif [ "${pop3_return:0:1}" = "+" ];then
echo ${pop3_return//$'\r'/}
continue
fi
mail_body="$mail_body
$pop3_return"
done <&3
echo "$mail_body"
echo -ne "QUIT\r\n" >&3
read pop3_return <&3
echo ${pop3_return//$'\r'/}
kill -9 `cat /var/tmp/stunnel.pid`
rm /var/tmp/stunnel.pid
exit 0
このstunnelによる暗号化は応用が効き、smtps、httpsでも同様に対応することができます。
なお、古いヴァージョンのSSLをサポートしているサーバはセキュリティ的に危険ということでSSLが拒絶して接続できない場合があります。こういう場合はセキュリティポリシーのレベルを下げてやります。rootで以下を実行します。
# update-crypto-policies --set LEGACY
Yahoo!は大丈夫だったと記憶していますが、ドコモのサイトがSSLの古いヴァージョンをサポートしているためSSLを使用するすべてのソフトでアクセス禁止になり、難儀したことがありました。
ここでの暗号化はstunnelで行いましたが、より一般的なツールであるopennsslでも可能です。両者は同一のライブラリで動作しているので大差はないと思われるもののopensslの方がより細かな設定が可能です。
第4項で示したスクリプトでも本稿の目的は達せられますが、当環境の実運用ではもう少しだけスクリプトの機能を拡充してあります。詳細はリンク先の実物を参照してもらうとして、機能拡充の要点は大きく三つあります。
実運用スクリプト(pop3s.sh)
1. クライアント側のコマンド出力の表示
第4項のスクリプトではクライアント側のコマンド出力は端末に表示されませんでしたが、実運用のスクリプトでは表示されるようになっています。実際にはこのスクリプトを端末から実行することはないので無用なものですが、主にデバッグ用です。
2. 使用ポートのための空きポート探索
第4項のスクリプトでは使用ポートを10005番に決め打ちしていましたが、実運用のスクリプトでは空きポートを探し、それを使用するようにしています。使用ポートが固定されていないためstunnelの設定ファイルも動的に生成しており(実際にはファイルは生成せず)、その設定を"-fd 0"オプションで標準入力からstunnelに読み込ませています。
3. 取得するメールの選択
第4項のスクリプトでは取得するメールはナンバー1のメールに決め打ちされていましたが、実運用のスクリプトでは、STATコマンドでメールボックス内のメール数を調べ、最新のメールを取得するようにしています。
当環境では当該のスクリプトをcronによる一日一回の定期実行で運用しています。
本稿で示したスクリプトはすべて自己解決・個別解決を目的としたものなのでエラー処理についてはほとんど考慮されていません。その分、簡便であり、その簡便さが自己解決・個別解決の大きなメリットです。
2020年4月頃、突然メールが送れなくなりました。アカウントの設定が変更されているようで何かの間違いか自分の設定ミスかと深く考えずに再設定で対応していたのですが、何日かするとまたリレー拒否に設定が変更されてしまいます。さすがにおかしいと思い、調べてみればWebメールのメールボックスにその旨を報せるメールが届いていました。スパムしか来ないYahoo!のWebメールなんていちいちチェックしていないので気づくのに少し時間がかかってしまいました。
そんな経緯から今回はPOPアクセスを自力で自動化してみましたが、もしかしたらそういうソフトも探せばあるのかもしれません。しかし、そういうクライアントソフトの学習コストは意外に小さくなく、かつ、自分が必要としている機能と微妙にズレがあったりして、結局、今回のように自力解決した方が速いというのはよくある話です。不特定多数の利用を前提とするソフトでは汎用性を重視せざるを得ず、その分、どうしても対応力・柔軟性が不足しがちです。本稿はPOPアクセスについてのみ実証したものですが、問題解決における一般論の一つとして参考になれれば幸いです。