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

第37回 MOPS:PHPにおけるコード実行(1)

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

第32回 PHPセキュリティ月間(Month of PHP Sercurity)「PHPセキュリティ月間」MOPS - Month of PHP Securityについて簡単に紹介しました。

今回もMOPS関連の話題です。MOPSではPHP関連のセキュリティ製品やセキュリティ知識の論文を募集し,11の論文が公開されました。今回はArthur Gerkis氏が投稿したPHPにおけるコード実行を解説した文書を紹介します。

MOPS Submission 07: Our Dynamic PHP - Obvious and not so obvious PHP code injection and evaluation
http://www.php-security.org/2010/05/20/mops-submission-07-our-dynamic-php/index.html

なぜPHPコード実行が起きるのか?

PHPコード実行が起きる原因は不適切なプログラムが原因であることがほとんどです。不適切なプログラムが作成されてしまう原因のほとんどはプログラマの知識不足です。⁠Our Dynamic PHP - Obvious and not so obvious PHP code injection and evaluation」⁠我々のダイナミックなPHP ─ 明白および不明確なPHPコードの挿入と実行)はタイトルのとおり「明白なコード実行」「不明確なコード実行」がどのように起きるのか解説しています。

今回の記事は,この文書に書かれている内容の要約に筆者の所見を追加した体裁となっています。

PHPにおけるコード実行

PHPは動的な言語であるため,eval関数へのパラメータに不正なコードを挿入し,不正なコードを実行することが可能です。PHPにおけるコード実行はeval関数へのコード挿入だけではありません。

明白なケース ─ eval関数

最も明白なケースはeval関数のパラメータへのコード挿入です。

<?php
eval("echo $foobar;");
?>

このコードの場合,ダブルクオートで囲まれたパラメータを渡しているので変数$foobarの内容はPHPが置換し,eval関数は$foobarの中身の文字列がPHPのコードとして評価されます。例えば,$foobarが

''; system('ls')

であれば,

echo '';  system('ls');

が実行されます。

<?php
eval('echo $foobar;');
?>

この場合,シングルクォートで囲まれた文字列をパラメータを渡しているので,eval関数は'echo $foobar'を評価します。結果は

<?php
echo $foobar;
?>

を実行した場合と変わりありません。

eval関数を実行した場合に気を付けなければならないことは,evalで評価したPHPコードで生成された変数は,evalの実行が終了した後にも残ってしまうことです。この仕様はセキュリティ上の問題となる可能性があります。Gerkis氏はeval関数のパラメータを適切にエスケープするのは非常に難しいので,できる限りevalの使用は避け,もしevalの使用が不可避な場合は文字リテラルを利用(つまり変数は利用しない)することを勧めています。変数が必要な場合は変数を初期化し,何か不具合があった場合に備えてユーザ定義エラーハンドラを使用すべきだ,としています。筆者もGerkis氏の意見に異論ありません。

明白なケース ─ スクリプトのインクルード

PHPはinlcude/include_once/require/require_once文でスクリプトを読み込むことができます。実は外部スクリプトの読み込みの問題はPHPに限ったことではないのですが,PHPが埋め込み型言語であるため,外部スクリプトの読み込みに対して簡単に脆弱になってしまいます。この問題についてはこの連載でも解説していますが,今回も解説します。

外部スクリプトの読み込みの問題は2つの種類に分類できます。

  • ローカルファイルの読み込み(LFI)
  • リモードファイルの読み込み(RFI)

この種類の攻撃を防ぐ最良の対策は「動的なパス」を利用しないことです。

Gerkis氏はファイルの読み込みに定数を利用することはよいアプローチだとしています。

<?php
define('APP_PATH', '/var/www/htdocs/');
require_once(APP_PATH . 'lib.php');
?>

そして,動的なファイル読み込みはすべきではないとしています。

<?php
$to_include = $_GET['file'];
require_once($to_include . '.html');
?>

これは典型的なファイルインクルード脆弱性です。文書の中ではDATA URIを利用した攻撃が紹介されています。上記のような脆弱なコードに対して

http://www.example.com/index.php?file=data:text/plain,<?php phpinfo();?>%00

等とするとPHPのコードが実行できてしまいます。DATA URIを利用した攻撃は脆弱なHTTPリダイレクトを利用したJavaScriptインジェクションの例が有名です。

Location: data:text/html;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5jb29raWUpPC9zY3JpcHQ+
※1

「PHNjcmlwdD5hbGVydChkb2N1bWVudC5jb29raWUpPC9zY3JpcHQ+」「<script>alert(document.cookie)<script>」をBASE64エンコードしたテキスト

などとしてJavaScriptインジェクションが可能でした。この攻撃に脆弱なWebアプリケーションがあまりに多いため,現在ではブラウザがこのようなリダイレクトでJavaScriptを実行できないように対策を行っています(PHPに限らずRailsアプリなど,スクリプト系アプリの多くのリダイレクトが脆弱です)⁠

これと同様の攻撃がPHPのinclude文に対して行えます。include文に対するDATA URIを利用した攻撃は非常に危険です。従来,ローカルファイル実行の問題とされていた脆弱性の問題の一部はリモートファイル実行と同様に任意のリモートからのスクリプトを実行可能です。

著者プロフィール

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

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

URLhttp://blog.ohgaki.net/

著書

コメント

コメントの記入