9-2 インクルードへの攻撃と防護策 [PHPセキュリティー]
インクルード
ディレクトリトラバーサル攻撃
- ディレクトリトラバーサルとは「../」を利用した不正操作や、それによって発生する脆弱性
- ディレクトリトラバーサルが発生すると機密情報が流出したり、コードインジェクションが発生する
ディレクトリトラバーサルとは「../」を利用してディレクトリを遡り、アプリケーションが意図しないアクセスが禁止されているディレクトリにアクセスされてしまったり、不正なコードを実行されてしまう脆弱性のことです。
上記で紹介したinclude系の関数が狙われます。include系の関数へ動的な値を渡している場合に、その値に「../」が含まれていることで、ディレクトトラバーサル攻撃が起こります。動的にインクルードするファイル名に汚染リスクがあると非常に危険です。
このような手法によって、機密情報が盗まれたり、悪意あるコードを書き込まれたり(コードインジェクション)といった攻撃へと発展します。
ファイル名改竄(かいざん)によるディレクトリトラバーサル攻撃
- ファイル名に「../」を含めて不正に情報を引き出す
入力データを使用して動的なインクルードを行う場合、入力データのパス名やファイル名に悪意のある内容が含まれている可能性があります。
例えば以下のようにPOST変数を使用してファイルを動的にインクルードします。POST変数は入力データです。
1 | <?php include_once("/cache/{$_POST['username']}.html"); ?> |
もし、$_POST[‘username’] の内容が ../admin/users だった場合、
1 | <?php include_once("/cache/../admin/users.html"); ?> |
となります。
これではアプリケーションが意図しないファイルにアクセスされてしまう可能性が発生します。これが成立すると情報漏えいへと発展します。
動的にインクルードする場合は汚染リスクのあるデータを使用してはいけません。適切にフィルタされたデータのみ使用するようにしましょう。
ディレクトトラバーサル攻撃によるコードインジェクション
ディレクトリトラバーサルを利用して、不正なコードを読み込ますことも可能です。これが成功すると攻撃者が用意したコードが実行され、コードインジェクションが発生します。
PHPではたくさんのタイプのリソースを、あたかもローカルファイルのように参照することができます。外部のPHPファイルを読み込むことも出来ますし、特定のウェブページの内容(HTML)をURLから読み込むことも出来ます。
1 2 3 4 | <?php include_once("/{$_GET['path']}/header.php"); require_once("https://wepicks.net/"); ?> |
このような場合に読み込む値が汚染されていれば、攻撃者が容易したコードが読み込まれ、実行されるコードインジェクションへと繋がります。
ディレクトトラバーサル攻撃への防御策
- 動的なインクルードを行う場合、ファイル名やパス名は適切にフィルタする
- basename()関数 pathinfo()関数 realpath()関数
ファイル名やパス名を適切にフィルタすることでディレクトトラバーサル攻撃を防ぐことができます。ファイル名やパス名の妥当性をチェックするのに以下の関数が利用できます。
basename()関数
通常ファイル名にパス情報が含まれているはずはありません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?php //入力データ $_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()関数
ファイル名やパス名をフィルタする際にこの関数を利用してファイルパスの内容を取得できます。
取得した内容を利用してフィルタを行うことが出来ます。
1 2 3 4 5 6 7 | <?php $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 以降 ?> |
結果は
config.inc.php
php
config.inc
となります。
realpath()関数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?php //入力データ $_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を入力として扱い、使う前にフィルタしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <?php //入力値 $_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などに使用すればディレクトリトラバーサルを避けることが出来ます。
タグ(=記事関連ワード)
日付
最終更新日:2017年08月09日