PHP はバージョン 8.1 から OpenSSL 3 に対応しています。
https://www.php.net/manual/ja/openssl.requirements.php
この OpenSSL 3 ですが、2021/9/7 にリリース されています。Quic などの新しいプロトコルへの対応をしていくということなので、大変喜ばしいことですが、知っておかないと困ることがあります。
OpenSSL 3: Support of SSL_OP_IGNORE_UNEXPECTED_EOF context option · Issue #8369 · php/php-src
OpenSSL 3 は unexpected EOF
に対して厳密に処理をするようになったということで、close を送ってこないサーバーなどに対して送信エラーになってしまいます。
実際に PHP のイシューに上がっていたサンプルコードを、OpenSSL 3 を使ってコンパイルした PHP 8.1.5 で実行するとこうなります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| $ php -v PHP 8.1.5 (cli) (built: Apr 24 2022 13:14:36) (NTS) Copyright (c) The PHP Group Zend Engine v4.1.5, Copyright (c) Zend Technologies with Zend OPcache v8.1.5, Copyright (c), by Zend Technologies with Xdebug v3.1.3, Copyright (c) 2002-2022, by Derick Rethans $ php -r "echo file_get_contents('https://chromedriver.storage.googleapis.com/LATEST_RELEASE', false, stream_context_create());" PHP Warning: file_get_contents(): SSL operation failed with code 1. OpenSSL Error messages: error:0A000126:SSL routines::unexpected eof while reading in Command line code on line 1 PHP Stack trace: PHP 1. {main}() Command line code:0 PHP 2. file_get_contents($filename = 'https://chromedriver.storage.googleapis.com/LATEST_RELEASE', $use_include_path = FALSE, $context = resource(4) of type (stream-context)) Command line code:1 PHP Warning: file_get_contents(): SSL: Success in Command line code on line 1 PHP Stack trace: PHP 1. {main}() Command line code:0 PHP 2. file_get_contents($filename = 'https://chromedriver.storage.googleapis.com/LATEST_RELEASE', $use_include_path = FALSE, $context = resource(4) of type (stream-context)) Command line code:1
|
今まで普通に使えていた REST API が突然接続エラーになるということも起こりそうです。
回避策
ウェブサーバー側が、正しく close を送るように対応するのが良いでしょうが、現状では未対応のサーバーが大量にあるため、現実的ではありません。
PHP 8.1.7 で後方互換性維持のための修正がはいりました。
https://github.com/php/php-src/commit/74f75db0c3665677ec006cd379fd561feacffdc6
SSL_OP_IGNORE_UNEXPECTED_EOF
が無条件でコンテキストに追加されるので、8.1.7以降であれば今までと同じ挙動に戻ります。
実際に実行してみると…
1 2 3 4 5 6 7 8
| $ php -v PHP 8.1.7RC1 (cli) (built: Jun 1 2022 08:43:27) (NTS) Copyright (c) The PHP Group Zend Engine v4.1.7RC1, Copyright (c) Zend Technologies with Zend OPcache v8.1.7RC1, Copyright (c), by Zend Technologies with Xdebug v3.1.5-dev, Copyright (c) 2002-2022, by Derick Rethans $ php -r "echo file_get_contents('https://chromedriver.storage.googleapis.com/LATEST_RELEASE', false, stream_context_create());" 102.0.5005.61
|
PHP 8.0 系以前は、OpenSSL の 1.1.1 系未満になるので、特に問題ないと思います。8.1.7 リリース後は速やかにアップグレードしたほうが良さそうです。
OpenSSL の上記の挙動は、 1.1.1e でも試験的に導入されたようなので、タイミングが悪い人は 8.0系のPHP を OpenSSL 1.1.1e で使うというニッチなセットになってしまい。上記不具合が再現されそうです。
おまけ
Truncation Attack とはなにか
そもそも、OpenSSL 3 が unexpected EOF に対して厳密になった理由は、Truncation Attack を回避するためということなのですが、そもそもこれが分からない。
プロフェッショナルSSL/TLS – 技術書出版と販売のラムダノート
とりあえず、上記書籍には 6.7 強制切断攻撃ということで載っていました。close_notify
を監視することで、強制切断による接続断なのか、正しい接続断なのかを判断するということらしいです。
OpenSSL 3 では、この close_notify
の監視を厳密化したということのようです。この攻撃を成功させるのは難しいようですが、セキュリティリスクに敏感なサービスにおいては、PHP での TLS 接続においても SSL_OP_IGNORE_UNEXPECTED_EOF
をあえて外して厳密化すると良さそうです。