PHPの本を読んでいたらバイナリセーフって単語が出てきてバイナリセーフなんだよ!? っとなりました。
よくわからないので自分なりに調べてみました。
バイナリセーフな関数
バイナリデータを正しく扱うことができる関数
バイナリセーフでない関数
バイナリデータを正しく扱うことができない関数
そもそもバイナリデータとは?
コンピュータが扱えるよう2進法に則って0と1の羅列(ビット列)として表現されたデータうち、テキスト(文字)形式ではない任意のデータ形式のこと。
※http://e-words.jp/w/%E3%83%90%E3%82%A4%E3%83%8A%E3%83%AA.html より引用
結局、気をつけることは何?
バイナリセーフでない関数を使用するとNULLバイト攻撃にあう
Nullバイト攻撃とは?
nullバイト(「\0」、「\x00」などの終端文字とされている文字列)を含めることでセキュリティチェックをくぐり抜けてしまう。
ロケールに基づく文字列比較の関数(strcoll、strcmp)を使用して確認してみる。
返り値はstr1 が str2 よりも小さければ < 0 を、str1が str2よりも大きければ > 0 を、 等しければ 0 を返します。 ※strcmpの公式リファレンスより
サンプルコード
<?php
// ロケールを日本語にする
// ※strcollはロケールが C または POSIX の場合、strcmpと等価なのでロケールの設定をする
setlocale (LC_COLLATE, 'ja_JP.UTF-8');
// 現在のロケールを表示する
echo setlocale(LC_ALL, 0) . PHP_EOL . PHP_EOL;
// 比較対象の文字列をセットする
$str1 = 'こんにちわ\x00ゴリラくん';
$str2 = 'こんにちわゴリラくん';
// strcoll ※バイナリセーフでない関数を使用
// 結果として、「2147483647」を返す(str1の方が大きいと判断している)
echo 'strcoll:' . strcoll($str1, $str2);
echo PHP_EOL;
// strcmp ※バイナリセーフな関数を使用
// 結果として、「-1」を返す(str2の方が大きいと判断している)
echo 'strcmp:' . strcmp($str1, $str2);
出力結果
LC_COLLATE=ja_JP.UTF-8;LC_CTYPE=Japanese_Japan.932;LC_MONETARY=C;LC_NUMERIC=C;LC_TIME=C
strcoll:2147483647
strcmp:-1
strcollの時は「\x00」が終末文字として認識されないため、str1が大きいと判断されるがstrcmpの時は「\x00」が終末文字として認識されるため、str2の方が大きいと判断される
気をつけないとNULLバイト攻撃された時にプログラムが意図しない挙動をしそうです。