PHPはスクリプトアップロードに弱いシステムですが、PHPアプリにはファイルアップロードをサポートしているアプリが数多くあります。WordPressなど自動更新を行うアプリも増えてきました。
PHPアプリの場合、MVCフレームワークなどを使っていてもエントリポイントにはPHPファイルが必要です。ファイルアップロードをより安全に使うための設定も可能ですが、WordPressのようなファイル配置で自動更新を行っているアプリの場合、攻撃を完全に防ぐ事ができません。
しかし、簡単な方法でドキュメントルート以下のPHPファイルの実行をホワイトリストで防御することができます。
防御の方法
以下のような手順で意図しないPHPスクリプトの直接実行を防止です。
- 実行を許可するPHPファイルのホワイトリスト(allowed_php_scripts.php)を作成する
- ホワイトリスト確認スクリプト(script_validator.php)を適切な場所に配置する
- php.iniの”auto_prepend_file”設定にホワイトリスト確認スクリプト(script_validator.php)を登録する
コレだけでドキュメントルート以下に配置されている意図しないPHPスクリプトを直接アクセスして実行する攻撃を完全に防げます。※
※ 注意:include/require文のファイル名生成の脆弱性はこの方法では防げません。include/require文で読み込むファイル名には十分注意してください。
allowed_php_scripts.phpの作成
allowed_php_scripts.phpにはアクセスを許可するPHPスクリプトの一覧を配列として定義します。
make_allowed_php_scripts.php
<?php $tmp = array_flip( array_map( function($e) {return chop($e);}, file("./allowed_php_scripts.txt") )); array_walk( $tmp, function(&$elem, $key) { $elem = filemtime($key); }); $scripts = var_export($tmp, true); file_put_contents( "allowed_php_scripts.php", "<?php \$allowed_php_scripts=". $scripts. ";" );
このファイルが/var/wwwに保存されているものとします。
[yohgaki@dev www]$ cd /var/www [yohgaki@dev www]$ find /var/www/html -name '*.php' -a -type f > allowed_php_scripts.txt [yohgaki@dev www]$ php -n make_allowed_php_scripts.php
この例では/var/www/以下にホワイトリスト定義(allowed_php_scripts.php)を作成しています。このホワイトリスト定義はドキュメントルートには配置しないでください。※
※ 配置しても問題ないですが、無用なファイルはドキュメントルート以下に配置すべきではありません。
”.php”以外にPHPスクリプトとして実行する設定にしている場合、これらのファイルもホワイトリストに追加してください。
allowed_php_scripts.phpの中身は、ファイル名がキー、更新時間が値となる以下のような配列になります。
<?php $allowed_php_scripts=array ( '/var/www/html/jpgraph/src/jpgraph_error.php' => 1285766041, '/var/www/html/jpgraph/src/jpgraph_rgb.inc.php' => 1285766041, '/var/www/html/jpgraph/src/jpgraph_flags.php' => 1285766041, '/var/www/html/jpgraph/src/imgdata_balls.inc.php' => 1285766041, '/var/www/html/jpgraph/src/jpgraph_scatter.php' => 1285766041, '/var/www/html/jpgraph/src/barcode/demoapp/barcode_menu.php' => 1285648506, '/var/www/html/jpgraph/src/barcode/demoapp/barcode_image.php' => 1285648506, 以下省略
更新時間チェックをしたくないファイル(自動更新されるファイルなど)は値を0にすると更新時間をチェックしなくなります。ただし、このようなファイルがあると攻撃対象になるので注意が必要です。PHPスクリプトにせずメモリキャッシュを使うなどの方法の方が安全です。
script_validator.phpの作成
このスクリプトもドキュメントルート以外の場所に配置します。ここでは/var/www/script_validator.phpを作成します。
<?php // 実行を許可するPHPファイルのホワイトリストを読み込む require_once('/var/www/allowed_php_scripts.php'); // ホワイトリストに載っていない場合、更新時間がセットされ異なる値であれば終了 if ( !isset($allowed_php_scripts[$_SERVER['SCRIPT_FILENAME']]) || ($allowed_php_scripts[$_SERVER['SCRIPT_FILENAME']] && filemtime($_SERVER['SCRIPT_FILENAME']) != $allowed_php_scripts[$_SERVER['SCRIPT_FILENAME']]) ) { error_log('PHP script upload attack from '.$_SERVER['REMOTE_ADDR']); http_response_code(404); die('PHP script upload attack detected'); }
php.iniの修正
/etc/php.iniなどのphp.iniファイルのauto_prepend_file設定を以下のようにします。
auto_prepend_file="/var/www/script_validator.php"
Webサーバーを再起動すれば防御が有効になります。.htaccessなどに書くこともできます。この場合、
php_value "auto_prepend_file" "/var/www/script_validator.php"
などと記述します。.htaccessがWebサーバーによって書き換え可能な場合、httpd.confのVirtualHostの設定とする方が良いです。この場合、.htaccessによる書き換え防止の為にphp_admin_valueを利用します。php_admin_valueの値は.htaccessでは変更できません。
php_admin_value "auto_prepend_file" "/var/www/script_validator.php"
テスト
例えば、ホワイトリスト作成時に存在しなかった/var/www/html/attack.phpを作成します。
touch /var/www/html/attack.php
ブラウザでアクセスするとホワイトリストに登録されていないので、以下のような出力になります。
PHP script upload attack detected
まとめ
allowed_php_scripts.phpはファイル名がキーとなるよう最適化されているので高速にチェックできます。ファイル数が多い場合でも、PHPスクリプトなのでopcacheなどを利用すればキャッシュされます。
この方法を利用すれば、仮に不正なPHPスクリプトファイルをドキュメントルート以下にアップロードされても、そのPHPスクリプトに直接アクセスして実行することができなくなります。最近レポートされたWordPress WPshop eCommerce 1.3.9.5 Shell Uploadのようなアップロード脆弱性にも対応できるようになります。※
※ 更新時間をチェックしているので、別の脆弱性を利用して更新時間を正しい時間に書き換えない限り、既存ファイルを書き換えても更新時間が異る為実行できない。
直接アクセスさせたくないPHPスクリプトファイルをドキュメントルート以下に置くべきではありませんが、もしドキュメントルート以下に直接アクセスさせたくないPHPスクリプトファイルがある場合、ホワイトリストから除外すればアクセスできなくなります。Webサーバー設定に依存しないので、$_SERVER[‘SCRIPT_FILENAME’]が正しく設定される環境であれば、どのWebサーバーでも使えます。
allowed_php_script.php、script_validator.phpファイルはWebサーバーから書き込めないようにしておかないと脆弱になります。ファイルのアクセス権限には注意してください。
参考