6月に入り、雨の多い梅雨の季節となりました。 今月は、深刻度の高いセキュリティ関連の修正が行われたPHPのリリースが行われています。また、11月のリリースに向けてPHPバージョン8.4の開発が行われており、間もなくα版のリリースが開始されます。今回の記事では、セキュリティ関連の修正に関する話題と共に、前回に続き、PHP 8.4で導入される予定の機能の一部を紹介します。
iconvに関する脆弱性と対策
CGI引数インジェクション攻撃に関する深刻度の高い脆弱性CVE-2024-4577が発見され、2024年6月6日にこの脆弱性を修正したPHPの各バージョンがリリースされました。 PHP 8.3系でPHP 8.3.8、PHP 8.2系でPHP 8.2.20、PHP 8.1系でPHP 8.1.29が対応するバージョンとしてリリースされています。この脆弱性は、Windows上で利用されているすべてのバージョンのPHPに影響するとされており、すでにこの脆弱性をついたサイバー攻撃も確認されているとのことです。関連するPHPを運用されている場合は、速やかに修正版にアップデートすることをお勧めします。
この脆弱性は、過去に修正されたCGI引数インジェクションCVE-2022-1823に対する修正を回避するもので、Unicode文字のソフトハイフン(0xAD)がPHP側で実際のハイフンとして変換されて解釈されることを利用して、ハイフンに対するサニタイズ処理を回避し、PHPのCGIに悪意のある引数を指定できるようにします。
この脆弱性は、以下の環境が影響を受けることが確認されています。
- Windows版でCGIモードのPHPを利用している。
- 中国語または日本語のロケールを利用している。
ただし、これ以外のロケールについても注意が必要とされており、Windows CGIモードを利用するPHPについては、アップデートが推奨されています。
配列で検索処理を行う関数の整備
PHP 8.4では、array_find() などの指定した配列の中で特定の条件を満たす要素を検索する関数が追加されます。PHPでは、すでに array_search() 関数をはじめ、類似の関数が存在しますが、クロージャ(無名関数)をコールバック関数として指定することにより、より柔軟かつ簡単に検索を行うことができます。追加されるのは、array_find()、array_find_key()、array_any()、array_all() の4種類の関数です。
順番に例を見てみましょう。まず、array_find()関数により、連想配列の要素を検索する簡単な例を示します。
<?php
$v = array_find($array, function (string $v) { return strlen($v) > 3;});
var_dump($v); // string(4) "taro"
この例では、クロージャにより、文字列長が3を超える要素を検索しています。array_find()関数は、複数の要素が一致する場合でも、一致した最初の要素(’taro’)を返します。
次に、類似のarray_find_key() 関数を使用するコードを追加してみましょう。
$v = array_find_key($array, function (string $v) {
return strlen($v) > 3;});
var_dump($v); // string(1) "a"
この関数の場合、クロージャには同じものを指定していますが、一致する要素のキー(’a’)を返します。
最後に、array_any()関数およびarray_all()関数の例を見てみましょう。
$b = array_any($array, function (string $v) {
return strlen($v) > 3;});
var_dump($b); // bool(true)
$b = array_all($array, function (string $v) {
return strlen($v) > 3;});
var_dump($b); // bool(false)
array_any()関数は、指定した配列のどれかがクロージャで指定した条件を満たす場合にtrue、それ以外の場合に false を返します。一方、array_all() 関数は、指定した配列の全ての要素が条件を満たす場合にtrue、それ以外の場合に false を返します。
この例の場合、文字列の長さが3を超える配列は、4つの要素のうちで2つありますので、array_any() 関数は true、array_all() 関数は false を返します。
クロージャの代わりに、PHP 7.4で追加されたアロー関数を指定して、より簡潔に記述することも可能です。以下の例では、連想配列の代わりに通常の配列、クロージャにアロー関数を指定しています。
$arr = ['taro','jun','hanako', 'jun'];
$v = array_find($arr, fn($v) => strlen($v)>3);
var_dump($v); // string(4) "taro"
round() 関数の丸めモード追加
PHP 8.4では、丸め処理を行う round() 関数にオプションが追加され、丸め処理のモードを指定できるようになります。新たに定義された以下の4種類のPHP定数を第3引数に指定することで、丸め処理の指定が可能となります。
PHP_ROUND_CEILING 第1引数よりも大きい最も近い整数に丸める
PHP_ROUND_FLOOR 第1引数よりも小さい最も近い整数に丸める
PHP_ROUND_AWAY_FROM_ZERO 第1引数をゼロから離れる方向に丸める
PHP_ROUND_TOWARD_ZERO 第1引数をゼロ方向に丸める
早速例を見てみましょう。第2引数に有効桁数0を指定して、4種類の丸め処理モードで実行します。
<?php
$number = -1.53;
var_dump(round($number, 0, PHP_ROUND_CEILING)); // float(-1)
var_dump(round($number, 0, PHP_ROUND_FLOOR)); // float(-2)
var_dump(round($number, 0, PHP_ROUND_AWAY_FROM_ZERO)); // float(-2)
var_dump(round($number, 0, PHP_ROUND_TOWARD_ZERO)); // float(-1)
入力の負の数値-1.53に対して、結果はそれぞれ -1、-2、-2、-1 となります。有効桁数を0とした場合、PHP_ROUND_CEILING、PHP_ROUND_FLOORの結果は、それぞれ、ceil()、floor()関数の結果と同じになります。
次に、有効桁数を1にしてみます。
var_dump(round($number, 1, PHP_ROUND_CEILING)); // float(-1.5)
var_dump(round($number, 1, PHP_ROUND_FLOOR)); // float(-1.6)
var_dump(round($number, 1, PHP_ROUND_AWAY_FROM_ZERO)); // float(-1.6)
var_dump(round($number, 1, PHP_ROUND_TOWARD_ZERO)); // float(-1.5)
結果は、順に -1.5、-1.6、-1.6、-1.5 となります。
最後に正の数1.53を指定してみましょう。
$number = 1.53;
var_dump(round($number, 1, PHP_ROUND_CEILING)); // float(1.6)
var_dump(round($number, 1, PHP_ROUND_FLOOR)); // float(1.5)
var_dump(round($number, 1, PHP_ROUND_AWAY_FROM_ZERO)); // float(1.6)
var_dump(round($number, 1, PHP_ROUND_TOWARD_ZERO)); // float(1.5)
結果は、順に 1.6、1.5、1.6、1.5 となります。入力の数の正負が変わると、4種類の丸めモードのうち、値の大小を考慮するPHP_ROUND_CEILING、PHP_ROUND_FLOORでは、符号だけでなく、絶対値も変わります。一方、ゼロへの方向を考慮するPHP_ROUND_AWAY_FROM_ZERO、PHP_ROUND_TOWARD_ZEROでは、符号は変わっても絶対値は変わりません。
暗黙のnullable引数型が廃止対象に
PHP では、nullになる可能性がある引数パラメータを明示的に指定する方法として、?T構文がPHP 7.1で導入され、PHP 8.0 で Union型として T|null 構文がサポートされています。しかし、それ以前に作成されたコードとの後方互換性のため、従来から存在する「暗黙のnullable指定」も利用可能となっています。これは、関数宣言時に null をデフォルト値として指定することで、array (配列)や callable 型のような本来はnullを指定できない引数であっても、暗黙のうちにnullを指定できるようにするというものです。
しかし、暗黙のうちにnullableとできる記法は、混乱を招く可能性があるため、廃止対象とする提案が行われ、投票により採用されました。
従来は、以下のように定義することができました。
function foo(array $var = null) {}
PHP 8.4 では、以下のような廃止対象に関する警告が発生するようになります。
Deprecated: foo(): Implicitly marking parameter $var as nullable is deprecated, the explicit nullable type must be used instead in …
上記のコードは、?T構文により以下のように変更することが推奨されています。
function foo(?array $var) {}
または、Union型により以下のように記載することも可能です。
function foo(array|null $var) {}
なお、この暗黙のnullable機能は、PHP 9.0で廃止される予定です。
今回は、PHPのセキュリティ関連の話題について紹介した後、前回に続いて11月のリリースに向けて本格的に開発が開始されたPHP 8.4における変更点の一部を紹介しました。次回以降もPHP 8.4のその他の変更点について紹介していきます。