バイナリセーフではない関数とは何か
「バイナリセーフではない」とは、特定の関数がバイナリデータを適切に処理できない事を意味します。
バイナリデータはテキストデータと異なり、nullバイト ('\0') などの特殊なバイトが含まれることがあります。PHPはC言語で作られており、C言語においてnullバイト ('\0')は終端を意味します。
つまりPHPでバイナリセーフではない関数を不適切に使用して、処理するデータにnullバイト ('\0')が混じっていた場合、文字列の終端を誤って処理され期待された結果が得られないことになります。悪用されるとセキュリティリスクにつながる可能性あります。
PHPでバイナリセーフではない関数の例
PHP8系以前でバイナリセーフではないとされている関数には、以下のようなものがあるようです。 (手元の環境PHP8.2ではおかしな挙動はありませんでした)
strpos関数
strpos
関数は、特定の文字列が別の文字列内で最初に出現する位置を見つけますが、
文字列内にnullバイト ('\0') がある場合、誤って文字列の終端として解釈する可能性があります。
不適切な使用例
このコードでは、strpos
関数がnullバイト ('\0') を文字列の終端として誤って解釈でき、バッファオーバーフローやセキュリティリスクを引き起こす可能性があります。
<?php $data = $_GET['data']; $search = "\0"; // Null byte (バイナリデータ) if (strpos($data, $search) !== false) { // Null byte が見つかった場合の処理 }
substr関数
substr
関数は文字列を部分的に切り出すために使用されますが、バイナリデータの場合、null バイトが含まれていると誤った結果が返される可能性があります。
不適切な使用例
このコードでは、バイナリデータのsubstr
関数を使用時に、nullバイト ('\0') が含まれている場合、正確な結果が得られず、セキュリティリスクが生じる可能性があります。
<?php $data = $_POST['data']; $offset = 0; $length = 10; $substring = substr($data, $offset, $length);
strlen関数
strlen
関数は文字列の長さを返しますが、nullバイト ('\0') を文字列の終端として扱うため、バイナリデータに対しては正確な結果を得られない可能性があります。
不適切な使用例
strlen
関数がnullバイト ('\0') を文字列の終端として扱うため、バイナリデータの長さを正確に測定できず、データの処理に問題を引き起こす可能性があります。
<?php $data = $_GET['data']; $length = strlen($data); // $length の値を信じてデータを処理
file_get_contents関数
file_get_contents
関数はファイルの内容を読み込むために使用されますが、バイナリファイルを扱う場合には、nullバイト ('\0') によって読み込みが早期に終了することがあります。
不適切な使用例
ユーザーからの入力をそのままファイル名として使用し、file_get_contents
関数に渡しています。これはセキュリティリスクを伴い、悪意のあるファイルを読み込んでしまう可能性があります。
<?php $filename = $_GET['file']; // ユーザーからのファイル名を使用 $fileContent = file_get_contents($filename); // $fileContent を処理
不適切に使用した場合のセキュリティリスクの例
バッファオーバーフローのリスク
バイナリデータを処理する場合、文字列終端を誤って判断したり、特定のバイトを誤って切り捨てたりすることがあるため、バッファオーバーフローのリスクが発生します。
これは、攻撃者が悪意のあるデータを送り込んでシステムに侵入する可能性を意味します。
セキュリティ無効化のリスク
バイナリデータに対してこれらの関数を使用すると、データの一部が誤って無視されたり、不適切に解釈されたりする可能性があり、セキュリティ上の問題が生じる可能性があります。
DoS攻撃のリスク
バイナリデータが正しく処理されない場合、無限ループに陥る可能性があるため、攻撃者はシステムを過負荷にかけるために不正なデータを送り込む事ができてしまいます。
まとめ
PHP8系以降ではバイナリセーフではない関数について公式のドキュメントが見当たらないので、 バイナリセーフではない関数についてPHP8.2で実際に試してみたところ、特におかしな挙動はありませんでした。 PHP側の方でセキュリティ対策が進んでいるのかも知れません。
ただし、何らかの事情により古いバージョンのPHPを使い続けなければならないシステムでは、これらの関数の取り扱いは要注意となります。
参考
体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 脆弱性が生まれる原理と対策の実践
「徳丸本」という愛称で有名な本です。 今回は徳丸本106ページに掲載されていたereg関数以外にもバイナリセーフではない関数って無いのか?と疑問を持ち、調べてみました。