ウェピックスではウェブ界の情報をピックアップします!
menu
ホーム > PHPセキュリティー > 9 インクルード > 9-2 インクルードへの攻撃と防護策

9-2 インクルードへの攻撃と防護策

Pocket

説明

インクルード
PHPでは、include()、include_once()、require()、require_once()、などの関数を利用して、外部ファイルを読み込むことが出来ます。また、fopen()関数ではURLをオープンすることが出来ます。これらの関数を利用することで、外部リソースを簡単に取り込むことができ非常に便利です。しかし、取り込む値が汚染されている場合に、適切なフィルタがされていなければ、アプリケーションの脆弱性を発生させます。これらの関数は注意して使用すべき関数です。

ディレクトリトラバーサル攻撃
POINT

  • ディレクトリトラバーサルとは「../」を利用した不正操作や、それによって発生する脆弱性
  • ディレクトリトラバーサルが発生すると機密情報が流出したり、コードインジェクションが発生する

ディレクトリトラバーサルとは「../」を利用してディレクトリを遡り、アプリケーションが意図しないアクセスが禁止されているディレクトリにアクセスされてしまったり、不正なコードを実行されてしまう脆弱性のことです。

上記で紹介したinclude系の関数が狙われます。include系の関数へ動的な値を渡している場合に、その値に「../」が含まれていることで、ディレクトトラバーサル攻撃が起こります。動的にインクルードするファイル名に汚染リスクがあると非常に危険です。

このような手法によって、機密情報が盗まれたり悪意あるコードを書き込まれたり(コードインジェクション)といった攻撃へと発展します。

ファイル名改竄(かいざん)によるディレクトリトラバーサル攻撃
POINT

  • ファイル名に「../」を含めて不正に情報を引き出す

入力データを使用して動的なインクルードを行う場合、入力データのパス名やファイル名に悪意のある内容が含まれている可能性があります。

例えば以下のようにPOST変数を使用してファイルを動的にインクルードします。POST変数は入力データです。

include_once("/cache/{$_POST['username']}.html");

もし、$_POST[‘username’] の内容が ../admin/users だった場合、

include_once("/cache/../admin/users.html");

となります。
これではアプリケーションが意図しないファイルにアクセスされてしまう可能性が発生します。これが成立すると情報漏えいへと発展します。
動的にインクルードする場合は汚染リスクのあるデータを使用してはいけません。適切にフィルタされたデータのみ使用するようにしましょう。

ディレクトトラバーサル攻撃によるコードインジェクション

ディレクトリトラバーサルを利用して、不正なコードを読み込ますことも可能です。これが成功すると攻撃者が用意したコードが実行され、コードインジェクションが発生します。

PHPではたくさんのタイプのリソースを、あたかもローカルファイルのように参照することができます。外部のPHPファイルを読み込むことも出来ますし、特定のウェブページの内容(HTML)をURLから読み込むことも出来ます。

include_once("/{$_GET['path']}/header.php");
require_once("http://wepicks.net/");

このような場合に読み込む値が汚染されていれば、攻撃者が容易したコードが読み込まれ、実行されるコードインジェクションへと繋がります。

ディレクトトラバーサル攻撃への防御策
POINT

  • 動的なインクルードを行う場合、ファイル名やパス名は適切にフィルタする
  • basename()関数 pathinfo()関数 realpath()関数

ファイル名やパス名を適切にフィルタすることでディレクトトラバーサル攻撃を防ぐことができます。ファイル名やパス名の妥当性をチェックするのに以下の関数が利用できます。

basename()関数
この関数はファイル名にパス情報の有無を確認する関数です。
通常ファイル名にパス情報が含まれているはずはありません。

//入力データ
$_POST['filename'] = 'filename.php';
//変数初期化
$clean = array();
//ファイル名フィルタ
if(basename($_POST['filename']) == $_POST['filename']){
	//初期化した配列変数に代入
	$clean['filename'] = $_POST['filename'];
	//インクルード
	include_once("/var/home/www/{$clean['filename']}");
}else{
	echo 'ファイル名が不正です。';
	exit;
}
pathinfo()関数
この関数はファイルパスに関する情報を取得します。
ファイル名やパス名をフィルタする際にこの関数を利用してファイルパスの内容を取得できます。
取得した内容を利用してフィルタを行うことが出来ます。

$path_parts = pathinfo('/home/www/inc/config.inc.php');
echo $path_parts['dirname'], "\n";
echo $path_parts['basename'], "\n";
echo $path_parts['extension'], "\n";
echo $path_parts['filename'], "\n"; // PHP 5.2.0 以降

結果は

/home/www/inc
config.inc.php
php
config.inc

となります。

realpath()関数
この関数は指定したファイルが存在する場合、正規化した絶対パス名を返します。返されるパスはシンボリックリンクや「/./」「/../」要素が含まれません。ファイルが存在しない場合は FALSE を返します。

//入力データ
$_POST['filename'] = 'filename.php';
//変数初期化
$clean = array();
//フィルタ後配列変数に代入
$clean['filename'] = $_POST['filename'];
//ファイルが存在する場合は正規化した絶対パスを返します。
$filename = realpath("/home/www/{$clean['filename']}");
// ファイルパス情報を取り出します。
$pathinfo = pathinfo($filename);
//ファイルパス情報の dirname と 意図しているパスが同一か確認する
if($pathinfo['dirname'] == '/home/www'){
	echo $clean['filename'].'は/home/www の中にあります。';
}

上記の関数を組み合わせてファイル名やパス名を適切にフィルタしてください。そしてフィルタしたデータのみでインクルードを指定してください。

以下の例では、読み込むファイル名は開発者が意図したファイル名になり、攻撃者が選んだものではないという保証を与え、このアプローチを強制するために、$contentsを入力として扱い、使う前にフィルタしています。

//入力値
$_GET['filename'] = 'file.php';

//変数初期化
$clean = array();
$html = array();

//入力$_GET['filename']を適切にフィルタする
$clean['filename'] = $_GET['filename'];

//ファイルを内容を読み込んで文字列として変数に格納
$contents = file_get_contents($clean['filename']);

//入力$contentsを適切にフィルタする。
$clean['contents'] = $contents;

//htmlエスケープ
$html['contents'] = htmlentities($clean['contents'], ENT_QUOTES, 'UTF-8');

//出力
echo $html['contents'];

フィルタされたデータのみをincludeやrequireなどに使用すればディレクトリトラバーサルを避けることが出来ます。

タグ(=記事関連ワード)

日付

投稿日:2012年7月22日
最終更新日:2012年08月29日

関連記事

このカテゴリの他のページ

この記事へのコメント

トラックバックurl

http://wepicks.net/phpsecurity-inc/trackback/