インクルードの脆弱性

インクルード攻撃では、リモートファイルインクルードと呼ばれる外部サーバのファイルを読み込ませる方法とローカルファイルインクルードと呼ばれるサーバ内部のファイルを読み込ませる方法が知られています。この手の攻撃を受けた場合、PHPのあらゆるコードが実行されてしまいます。

インクルードで攻撃される場所

リクエストから受け取った値をinclude、include_once、require、require_onceの引数に入れることで脆弱性が発生します。例えば、以下のようなコードを記述したPHPファイルのフォームから「https://リモート先URL」という形式で入力すると、PHPコードが実行されます。この例では$_POSTを使っていますが、$_GETであればURL最後尾に「?AttackFilePath=https://リモート先URL」と追記すれば同じことが実現できてしまいます。

// HTML
<form action="" method="post">
<input type="text" size="40" name="AttackFilePath" value="" />
<input type="submit" value="クリック" />
</form>
// PHP
include $_POST['AttackFilePath'];

インクルードの対応策

nclude、include_once、require、require_onceの引数にリクエストから受け取った値を入れなければこの脆弱性の問題は発生しません。また、リモートファイルのインクルードについては、php.iniの設定で「allow_url_include = 0」とすることで無効にできますが、デフォルトが無効です。そのため、この設定を変更していなければリモートファイルのインクルードは初めからできません。

どうしてもインクルードの引数にリクエストを渡す場合

インクルードファイルの数が少なければ、ホワイトリストで対応します。
PHP5.3.5以降のinclude(言語構造)などでは、引数のファイルパスにNullバイト文字が含まれれば処理が失敗するため、

// 許可したURL以外は処理を停止
if(in_array($_POST['AttackFilePath'],
array('許可したいURL'), true) === false)
{exit;}

特定のディレクトリ内で多くのインクルードファイルを制限する場合は、basename()を使います。この関数はバイナリセーフではないため、Nullバイト攻撃への対処も必要です。

// 文字列からNullバイト文字を削除
$_POST['AttackFilePath'] = str_replace("\0", "", $_POST['AttackFilePath']);
// Nullバイト文字が存在すれば処理を停止
if(strpos($_POST['AttackFilePath'], "\0") !== false)
{exit;}
// 文字列から不正な文字を削除
inlude '/インクルードするファイルのディレクトリまでのパス/'.
basename($_POST['AttackFilePath']).'.拡張子を指定';