なぜPHPアプリにセキュリティホールが多いのか?

第26回 まだまだ残っているファイル読み込みバグ

この記事を読むのに必要な時間:およそ 2 分

PHPのスクリプト読み込み用の関数である require/indclude文は,PHPが埋め込み型の言語であるために非常に危険なバグの温床となっています※1)。

※1

require/include文は本当は関数ではなく,言語の基本機能として実装されているif文などと同じ(ステートメント)です。

つい最近のファイル読み込みバグ

執筆時点(4/24)でレポートされているファイル読み込みバグです。

  • WebPotal CMS(4/23)
  • NotFTP(4/22)
  • TotalCalender(4/22)
  • SMA-DB(4/17)
  • Job2C(4/16)
  • FreeWebshop(4/16)
  • GuestCal(4/15)
  • Jamroom(4/15)

ファイル読み込みバグはサーバの乗っ取りも可能とする致命的なバグですが,現在でも脆弱性が次々にレポートされているバグであることが分かります。

require/include文とファイル読み込みバグの問題

PHPはスクリプト(プログラム)と出力を同じファイルの中に書き込める,埋め込み型の言語です。この仕様により簡単なWebアプリケーションであれば,非常に短いコードで記述することができます。しかし,この言語仕様が非常に深刻なセキュリティ問題を発生させる原因となっています。

ファイル読み込みバグがある場合,システムの上のファイルの読み取りだけでなく,任意スクリプトの実行を可能とします。つまり,サーバを完全に乗っ取られる可能性があるのです。

require/include文

require/require_once/include/include_onceでプログラムとテキスト(実際にはテキストに限らずバイナリも読み込み可能)を同時に扱えることは,セキュリティ上のリスクをもたらします。

PHP以外の言語では,プログラムとテキストを両方読み込める仕組みを持つ言語はあまりありません。プログラムとテキストを両方含む仕組みを持てるようにしている言語拡張は多くありますが,元々言語が持っている機能として持っている言語はあまりありません。

さらに問題なのは,PHPはプログラムだけを含むファイルを読み込む関数がありません。このため,プログラムだけを読み込みたい場合でも,プログラムとテキストの両方を読み込めるrequire/include文を使用しなければなりません。

テキストとプログラムを同じ文で読み込む危険性

経験豊富なプログラマでも,経験が少ないプログラマでも,include/require文でファイルを読み込む危険性を正しく理解していない場合が多いです。

PHP初心者がよく作ってしまう致命的なセキュリティホールには次のようなコードがあります。

if (!empty($_GET['plugin'])) {
  require_once(PLUGIN_DIR.DIRECTORY_SEPARATOR.$_GET['plugin'].'.php');
}

経験豊富なプログラマであっても,プログラムファイルだけ読み込んでいるつもりでこのようなコードを書いてしまうことがあります。実際にZend社のWebサイトにこれと同類のコードがサンプルコードとして掲載されていたことがあります。

このコードは非常に危険です。なぜなら,..を利用して上位ディレクトリのファイルにアクセスできる上,%00(ヌル文字)を利用して.php拡張子だけの読み込みに限定している部分も無効化できます。

つまりこのようなコードを書くと,権限を持つシステム上のファイルに自由にアクセスできるようになってしまいます。

テキストの読み込み

PHPは<?php ?>等で囲まれた部分以外はecho文で出力したのと同じ動作をします。

先ほど紹介したコードで$_GET['plugin']に../../../etc/password¥0等の文字列を設定することにより,システム上のファイルにアクセスできるようになります。

プログラムの読み込みだけ可能であれば,UNIXシステムのパスワードファイルである/etc/passwordファイルを読み込もうとしてもエラーになり,被害は発生しません。しかし,PHPのinclude/require文はプロブラム以外のテキストを標準出力に出力してしまいます。これはWebサーバの場合,ブラウザに出力する事を意味します。この仕様によりどんなファイルでも読み取って盗むことが可能になります。

コードの読み込み

requrie/include文はコードを読み込み実行できます。しかも,攻撃者に都合よいことに,コード以外の部分は標準出力に出力するので,エラー無くPHPコードを実行できます。

例えば,画像ファイル,PDF等のバイナリに埋め込まれたPHPスクリプトでもinclude/require文で読み込めば実行していまいます。

例:PHPスクリプトを含むgifファイル

GIF89a
<?php system('rm -rf /'); ?>

このファイルはgetimagesize関数ではgif画像として認識されてしまいますが,require/include文で読み込まれると,

system('rm -rf /');

を実行してしまいます。UNIX系のシステムではアクセス権限がある全てのファイルを消してしまいます。

テキストだけでなく,どんなバイナリファイルでもPHPコードを含ませることが可能です。任意のファイルが読み込めてしまうバグがあるために,ファイルアップロードを許可しているアプリでは致命的な欠陥となってしまいます。

リモートスクリプト

さらに問題を複雑にしているのがリモートスクリプトです。PHPはrequire/include文でほかのサイトにあるPHPスクリプトを実行できます。攻撃用のスクリプトを何らかの方法でアップロードしなくても攻撃できるのです。

この仕様の危険性はようやく認識され,PHP5.2ではデフォルトで無効となるように設定されました。

著者プロフィール

大垣靖男(おおがきやすお)

University of Denver卒。同校にてコンピュータサイエンスとビジネスを学ぶ。株式会社シーエーシーを経て,エレクトロニック・サービス・イニシアチブ有限会社を設立。
オープンソース製品は比較的古くから利用し,Linuxは0.9xのころから利用している。オープンソースシステム開発への参加はエレクトロニック・サービス・イニシアチブ設立後から。PHPプロジェクトでは,PostgreSQLモジュールのメンテナンスを担当している。

URLhttp://blog.ohgaki.net/

著書

コメント

コメントの記入