evalを使った場合の危険性

preg_replaceのe修飾子はPHP7以降で使えなくなっていますが、それより前のバージョンでは使えます。このe修飾子は、preg_replaceにおいて置換後のパターン文字列をPHPコードとして評価するという意味です。このようなe修飾子を使った関数やeval関数などは、受けとった文字列をPHPコードとしてさばきます。そのため、外部入力から渡される文字列に何の処理も加えていなければ、悪意のあるコードの代入によって攻撃が成立してしまう場合があります。

危険とされる理由

evalは外部からの入力文字を一切受け付けず、プログラム内部のみの文字列を扱うということが未来永劫、徹底した前提になっていれば、その利用に問題はないと思います。evalそのものは若干スピードが遅くなるものの利用条件によっては非常に便利なものなのです。巷では、evalは、危険な関数の一つになっていますが、これは大きなプログラムやそれを増設する過程で、処理する文字列がどこから入ってきて、どう対応されて、どこへ出ていくのかを認識できなくなる可能性を持っているからです。複数人が関わるプログラムならなおさらです。また、人間は時間の経過とともに物事の一部を忘れてしまうため、完璧というものはありません。従って、evalを使わないで対応できるのであれば、わざわざ処理が遅くてリスキーなevalを使う合理性が無いということになります。

攻撃される場所

不適切に処理された外部からの入力を受け付けるフォームなど。下記コードは一見、htmlspecialcharsでエスケープして攻撃に対応しているように見えますが、他のインジェクション攻撃とは基本的に原理が異なります。例えば以下のようなコードでフォームから「'.print date("Y/m/d").'」や「'.print microtime().'」などを入力するとタイムスタンプを出力します。これらはただの日付を表示する関数なので実害はありません。しかし、関数を実行できるということは、悪意あるコードを注入されて関数を実行されてしまうということでもあります。例えば不適切に対応されたフォームから「'.phpinfo().'」を入力すると、PHPのバージョンなどセキュリティーに関わる情報が出力されてしまいます。

// HTML
<form action="" method="post">
<input type="text" size="40" name="attack" value="" />
<input type="submit" value="クリック" />
</form>
// PHP
$str='ababab';
eval("echo htmlspecialchars(
str_replace('". $_POST['attack'] ."', '', '".$str."'),
ENT_QUOTES, 'UTF-8');");

上記の場合、POSTで送信していますが、これはGETで送信しても同様です。GETのケースでは、アドレスバーのURL最後尾に「?attack='.print date("Y/m/d").'」を追記すれば、dateが実行されます。

対処法

以下はホワイトリストによるチェックです。

$str='ababab';
if(strpos($str, $_GET['attack']) !== false){
eval("echo htmlspecialchars(
str_replace('". $_GET['attack'] ."', '', '".$str."'),
ENT_QUOTES, 'UTF-8');");
}

ホワイトリストでは、外部から受けたリクエストの文字列が、プログラム内で設定されている文字列($str)中に存在すれば処理を開始します。

また、strpos()は検索文字列が最初に現れる場所をチェックします。この関数は検索文字列を見つけるとその位置の数値(オフセット)を返します。そのため型まで調べなければならず、falseの場合は「=== false」、trueのケースは「!== false」を使います。