ゴールデンウイークの連休も終わり、暑さを感じる日が増えています。11月のリリースに向けて、次のマイナーバージョンであるバージョン8.4の開発が本格化しています。今回の記事では、セキュリティ関連の話題と共に、前回に続き、PHP 8.4で導入される予定の機能の一部を紹介します。
iconvに関する脆弱性と対策
GNU C Library (glibc) のiconvに関する脆弱性CVE-2024-2961に関する公式アナウンスが4月24日にphp.netに掲示されました。このバグは、PHP自体に起因する脆弱性ではありませんが、PHPがこの脆弱性を有する環境で使用された場合、PHPでiconv()関数またはフィルタ機能で文字コードを指定して利用する際、リモート攻撃により任意のコードを実行される可能性があるという意味で、深刻なものです。この脆弱性は、glibcのiconv関数における文字セットISO-2022-CN-EXTに関する処理に存在しており、以下がどれかを行うことが対策となります。
- 利用するシステムのglibcを関する上記の脆弱性に関する対策版に更新する
- gconv-modules-extra.conf で問題の文字セット(ISO-2022-CN-EXT)を除く
- iconv拡張モジュールを利用しない
- 問題のない文字セットのみを利用する
この脆弱性は、PHP自体に起因しないため、PHP本体へのセキュリティパッチや対策版のPHPのリリースは行われません。
問題のあるglibcを更新するのが本質的な対策となります。本稿執筆時点で、Debian, CentOS等のメジャーなLinuxディストリビューションでは修正版のglibcが配布されており、該当するシステムを運用している場合は、速やかに更新することをお勧めします。また、修正版が配布されていないシステムで運用を継続する必要がある場合は、上記の対策の内、glibc更新以外の対策を実施することをお勧めします。なお、Windows版のようにglibcを使用していない環境では、この脆弱性の影響は受けないため、対策は不要です。
巨大なXMLファイルのパースが可能に
PHPでは、XMLで記述されたデータを処理する複数の手段をサポートしています。SAXは、XMLデータをストリーム処理によりシーケンシャルにパースする最もシンプルなインターフェイスで、メモリ上にすべてのデータを読み込んで展開する必要がないため、大きなXMLデータを扱う際に特に有用です。
ただし、XMLタグについては最大サイズの制限がありました。例えば、以下のコードを従来のバージョンのPHPで実行してみましょう。
<?php
$text = str_repeat("ABCDE", 1000000);
$xml = "<?xml version=\"1.0\"?><message><$text/><$text/>
<boo>foo</boo></message>";
$parser = xml_parser_create();
$ret = xml_parse($parser, $xml);
if ($ret == 0) { /* on error */
echo xml_error_string(xml_get_error_code($parser));
} else {
echo "Success";
}
このスクリプトを実行すると出力は以下のようになり、パースエラーとなります。
XML_ERR_NAME_REQUIRED
これは、5Mバイトという巨大なXMLタグが指定されており、パース時にエラーとなったためです。
PHP 8.4以降では、XMLパーサのオプションXML_OPTION_PARSE_HUGEをtrueに設定することで、巨大なサイズのXMLタグも扱うことができます。
6行目の下に以下のコードを挿入し、再度、PHP 8.4の開発版で実行してみます。
xml_parser_set_option($parser, XML_OPTION_PARSE_HUGE, true);
出力は、「Success」となり、正常にパースが行われています。本稿では省略していますが、従来と同様に適当なコードバック関数を指定することで、SAXパーサによるXMLデータの処理を行うことができます。
日時処理用クラスDateTimeがより便利に
PHP 8.4では、日時に関する処理を行うクラスDateTimeおよびDateTimeImmutableに関して以下に示す機能強化が行われます。
- タイムスタンプからクラスを生成するcreateFromTimeStamp()メソッドの追加
- μ秒単位の処理を行うgetMicrosecond()、setMicrosecond()メソッドの追加
ここで、DateTimeクラスはミュータブル、つまり、オブジェクトの作成後も状態を変えることができます。一方、DateTimeImmutableクラスは、名前の通りイミュータブルで、オブジェクト作成度は状態を変えることができません。DateTimeクラスのオブジェクトのメソッドをコールすると、日時を表すオブジェクトの状態が変わってしまう可能性がありますが、特定の日時を表したい場合など、オブジェクトの状態を変更したくない場合は、DateTimeImmutableクラスを利用します。
通常、特定の日時を文字列としてコンストラクタに指定してDateTimeオブジェクトを生成します。
<?php
$date = new DateTime("2024-06-01 12:34:56 UTC");
var_dump($date);
生成されたDateTimeオブジェクトをダンプすると以下のように指定して日時とタイムゾーン(UTC)が表示されます。
object(DateTime)#1 (3) {
["date"]=>
string(26) "2024-06-01 12:34:56.000000"
["timezone_type"]=>
int(2)
["timezone"]=>
string(3) "UTC"
}
PHP 8.4以降では、DateTime(およびDateTimeImmutable)にcreateFromTimeStampメソッドが追加され、Unixタイムスタンプによる日時の初期化が可能になります。
早速以下の例を見てみましょう。
<?php
$dt1 = DateTime::createFromTimeStamp(1717245296);
echo $dt1->format('Y-m-d h:i:s'),PHP_EOL;
createFromTimeStampメソッドにUnixタイムスタンプ(整数またはfloat)を指定してスタティックコールすると、DateTimeオブジェクトが返されます。
formatメソッドにより整形した時刻出力は、”2024-06-01 12:34:56” となります。
次の例では、Unixタイムスタンプをfloatで指定しています。
<?php
$dt2 = DateTimeImmutable::createFromTimeStamp(1717245296.789);
echo $dt2->format('Y-m-d h:i:s.u'),PHP_EOL;
formatメソッドの出力は、”2024-06-01 12:34:56.789000” となり、μ秒単位の時間を指定できていることがわかります。
PHP 8.4では、μ秒単位の時間を指定または取得する仕組みとして、getMicrosecondメソッドおよびsetMicrosecondメソッドが導入されます。
μ秒単位の時間を取得するには、以下のようにgetMicrosecondメソッドを使用します。
<?php
$dt2 = DateTimeImmutable::createFromTimeStamp(1717245296.789);
var_dump($dt2->getMicrosecond()); // int(789000)
出力は、int(789000) となり、時刻の1秒未満の部分が、μ秒単位の整数で取得されます。
setMicrosecondメソッドにより、μ秒単位の時刻を設定することができます。以下の例を見てみましょう。
<?php
$date = new DateTime("2024-06-01 12:34:56 UTC");
$date->setMicrosecond(789012);
var_dump($date->getMicrosecond());
echo $date->format('Y-m-d h:i:s.u'),PHP_EOL;
従来の手法で日時を指定してDateTimeオブジェクトを生成した後、setMicrosecondメソッドにより、μ秒単位の時刻を設定します。getMicrosecondメソッドの出力は、int(789012)となり、指定したμ秒単位の時刻が設定されていることがわかります。また、formatメソッドによる整形した日時の出力は、”2024-06-01 12:34:56.789012” となり、やはり、設定したμ秒単位の時刻が反映されていることが確認できます。
今回は、PHPのセキュリティ関連の話題について紹介した後、前回に続いて11月のリリースに向けて本格的に開発が開始されたPHP 8.4における変更点の一部を紹介しました。次回以降もPHP 8.4のその他の変更点について紹介していきます。