目次

【PHP】imap_open で「Couldn't open stream」エラーが出る

メールサーバを自作し、imap_open でメールサーバにある受信メールを取得するプログラムを作成していた際に遭遇したエラーとその対処法について。

遭遇したエラー

SSL / TLS を使用して POP3 / IMAP でメールを取得しようとしたところエラーが発生。

Laravel でのエラー表示

ErrorException
imap_open(): Couldn't open stream {localhost:993/imap/ssl}INBOX

PHP エラーログ上の表示

PHP Fatal error: Uncaught ErrorException: Unknown: Certificate failure for localhost: Server name does not match certificate: /CN=example.com (errflg=2) in Unknown:0
Stack trace:
#0 [internal function]: Illuminate\Foundation\Bootstrap\HandleExceptions→handleError()
#1 {main}
thrown in Unknown on line 0

/var/log/maillog の内容

dovecot: imap-login: Disconnected (no auth attempts in 0 secs): user=<>, rip=127.0.0.1, lip=127.0.0.1, TLS, session=<XXXXXXXXXXXXXXX>

現象

Postfix と Dovecot に SSL/TLS を導入する前は imap_open が成功していました。 (POP3:110番ポート、IMAP:143番ポート)

$account = 'mail@example.com';
$password = 'PASSWORD';

# POP3 で接続(成功)
$mailbox = '{localhost:110/pop3/notls}INBOX';
$mbox = imap_open($mailbox, $account, $password)
    or die('接続エラー: ' . imap_last_error());
imap_close($mbox);

# IMAP で接続(成功)
$mailbox = '{localhost:143/imap/notls}INBOX';
$mbox = imap_open($mailbox, $account, $password)
    or die('接続エラー: ' . imap_last_error());
imap_close($mbox);

ところが、SSL/TLS を導入後 imap_open でエラーが出るようになりました。 (POP3S:995番ポート、IMAP4S:993番ポート)

# POP3S で接続(失敗)
$mailbox = '{localhost:995/pop3/ssl}INBOX';
$mbox = imap_open($mailbox, $account, $password)
    or die('接続エラー: ' . imap_last_error());
imap_close($mbox);

# IMAP4S で接続(失敗)
$mailbox = '{localhost:993/imap/ssl}INBOX';
$mbox = imap_open($mailbox, $account, $password)
    or die('接続エラー: ' . imap_last_error());
imap_close($mbox);

解決方法

「/novalidate-cert」を付けるだけ。。。

# POP3S で接続(成功)
$mailbox = '{localhost:995/pop3/ssl/novalidate-cert}INBOX';
$mbox = imap_open($mailbox, $account, $password)
    or die('接続エラー: ' . imap_last_error());
imap_close($mbox);

# IMAP4S で接続(成功)
$mailbox = '{localhost:993/imap/ssl/novalidate-cert}INBOX';
$mbox = imap_open($mailbox, $account, $password)
    or die('接続エラー: ' . imap_last_error());
imap_close($mbox);

デバッグ情報を表示するようにしたり、色々試した結果、最終的には PHPの公式ドキュメントにあるコメント の記述に助けられ、無事動くようになりました。

公式ドキュメントには「/novalidate-cert」オプションは、自己証明書を使っている際に必要、とあったので、Let's Encrypt を使用していた今回は関係ないと思っていたのですが、原理は分からないままなものの、このオプションを付けることで成功するようになりました。

ちなみに、Thunderbird などのメールソフトでは正常に送受信に成功しているので、証明書が間違っているということはありません。
なぜだ。。。