スクリプトアップロード対策(追加)

Computer, PHP Security, Security 12月 2, 2013
(Last Updated On: 2018年8月14日)

スクリプトアップロード対策は既に書きましたが、書き漏らしていたので追加します。

よくあるWebサーバーはスクリプトが保管されているディレクトリに書き込み権限を持っていません。しかし、WordPressは自動更新やユーザーによるプラグインのインストールなどが行えるようになっています。明らかに単純ミスと思われる物もありますが、全てのプラグインが「書き込み可能なスクリプトディレクトリ」を想定している訳ではないと思います。wp-contentディレクトリへのスクリプトアップロード脆弱性が多数報告されています。

wp-content/uploadsでPHPを無効にするだけでなく、wp-content/plugins, wp-content/themesディレクトリへの書き込み許可はプラグイン/テーマをインストールする時だけ有効にする方が良いと思います。ただし、プラグイン/テーマのプログラムが書き込み可能であることを前提にしている場合もあるかも知れないことに注意してください。

WordPressの自動アップデート、Web UIからのプラグインインストールは便利なのですが、普段書き込みができる必要のないディレクトリ/ファイルに対してはWebサーバーが書き込みできないようにし、管理者・システムがアップデート/インストールする場合のみ書き込み権限を与えるようにした方が良いと思います。

プラグインやテーマのディレクトリだけ書き込み禁止してもダメです。上記の脆弱性報告を見れば分かりますが、プラグイン/テーマがインストールしたサブディレクトリの書き込みも禁止しなければなりません。インストールしているプラグインやテーマによってどのディレクトリを書き込み禁止にできるかは変わります。WordPress本体がインストールしたファイル/ディレクトリの書き込み権限もアップデート時以外は無い方が良いです。環境に合わせて調整しましょう。

プラグインやテーマ、アップロードしたファイルはドキュメントルート以外に配置すれば良いのですがWordPressの場合、歴史的に難しいとは思います。リクエストのリライトを前提にすれば互換性を保ちながらドキュメントルートにアプリのファイルのみを配置可能ですが、様々なWebサーバーに対応するには手間がかかります。多少面倒ですが次かその次のマイナーバージョンアップにはこうなっていると嬉しいです。

自分でファイルアップロードに脆弱性がある可能性があるPHPファイルを検索するには

  • $_FILES
  • php://input

を検索し、コードの中身を確認すれば概ね確認できます。”概ね確認”できるのは$_POST, $_GETなどの内容をファイル/スクリプトとして書き出す事も可能だからです。

ファイル書き出しには色々なコードがありますが、今時のスクリプトならfile_put_contetnsを利用しているとと思います。こちらも確認すればより安全です。

Webサーバー・アプリが自由にファイルを書き込みできないようにする方が安全です。しかし自由に書き込み可能であるほうがかなり便利です。私は取り敢えずアップデート時に権限を切り替え、通常運用時はホワイトリスト方式で書き込み可能なファイル/ディレクトリを指定するようにしました。

まとめ

ファイル/スクリプトアップロード対策にもセキュリティの原則である「最小権限の原則」が有効です。

  • スクリプトの実行が必要な場所でのみ実行を許可する
  • ファイル書き込みが必要な場面(アップデート/インストール)でのみ書き込みを許可する

これだけ実践しているだけでもかなりリスクを低減できます。もう一つ必要な事はプログラムをアップロードさせない事です。

  • ドキュメントルート以下にアップロードを許可する場合、プログラムとして実行できないファイルのみ許可する(サーバーサイドスクリプト、HTML、CSS、JavaScriptなどのファイルは厳禁)

楽しく、セキュアにコーディングしましょう!

 

おまけ

PHPはどんなファイルにもプログラムを埋め込めるため、スクリプト実行のリスクが高くなっています。アップロードされたファイルをより安全に保存する簡単なテクニックを紹介します。ドキュメントルートに保存する場合も、直接アクセスしないことが前提条件です。

ファイルを保存する場合に必ず先頭に

<?php noaccess() ?>

などの文字列を入れます。noaccess()は次のような関数にします。

<?php
function noaccess() {
  trigger_error(E_USER_ERROR, 'Direct access. Possible attack');
  exit 1;
}
?>

実際にファイルを利用する場合、file_get_contents()などでファイル取得し”<?php noaccess() ?>”を取り除きます。

大きなファイルなどでメモリの制約がある場合はfopen/freadを使うかstream wrapperを作成して”<?php noaccess() ?>を取り除きます。

単純に

<?php die('Should not access directly') ?>

としない事にはセキュリティ的な理由があります。

セキュリティ問題(つまり攻撃)を防止するだけがセキュリティ対策ではありません。「攻撃を検出」する事も重要なセキュリティ対策です。noaccess()ならエラーログにエラーが記録されます。ここでは省略しますが、実際にはエラーハンドラなどで、IPアドレス、セッションの内容、リクエストの内容なども記録します。

 

投稿者: yohgaki