メニューボタン
IBMi海外記事2008.03.13

PHP Webアプリケーション用に安全な環境を構成する

アラン・セイデン著 著

System iは、よく知られたWebプログラミング言語であるPHPをサポートしたことにより、PHPベースのさまざまなWeb用ソフトウェアを実行することができます。インターネットのオープンな環境を考えると、IBMのZend Core for i5/OS環境にWebアプリケーションを導入している慎重なシステム管理者であれば、セキュリティを確実なものにしたいでしょう。では、どういった予防措置が必要なのでしょうか。

System iのアーキテクチャは、バッファ・オーバーラン、ウィルス、ワームから自動的に保護するようになっており、Zend CoreのPHPは一般のPHPが持っている予防手段の他にも追加で予防手段を提供していますが、さらに、以下のような脅威に対して予防手段を用意しておくべきです。

  • ブラウザへのウィルス増殖(サイトそのものは免疫されていても)
  • パスワードの「スニッフィング」(盗み取り)
  • アプリケーションの不正な実行
  • 機密データの開示や改竄

こうした予防手段の中にはPHPプログラミングの特別なテクニックを必要とするものがあり、これらについてはいずれご紹介します。今回は、PHP環境の中で守備範囲の広い保護手段を講じる方法について説明します。

注記: Webのセキュリティは急速に進化している分野です。ここでご紹介する推奨事項はいずれも、Zend Core for i5/OS version 2.0.1 (i.e., PHP 5.2.1)に対応した内容となっています。

防衛の最前線

どのようなアプリケーションを実行していようとも、実行環境を安全なものにしておくことで、既知の問題や未知の問題に対するリスクを低減することができます。マイケル・サウズウェル氏と共著でPro PHP Security (Apress 2005年発行)を出版し、PHPのセキュリティの専門家でもあるクリス・スナイダー氏は、複数のレベルでの保護、いわゆる「厚みのある防衛」を勧めています。なぜなら、「どこで間違えるかわからない」からと言います。Fund for the City of New YorkのWebデベロッパーであるスナイダー氏は、Webアプリケーションの安全性は、それが実行されている環境の安全性と同じかそれ以下にしかなりえない、と考えています。

PHPの実行環境はアプリケーションのセキュリティの外周です。可能であればここで攻撃を食い止めたいでしょう。本稿では、PHPやアプリケーションのパッチ保守、暗号化、ディレクトリ構造、構成ファイル、PHPの定期更新など、防衛の外輪について説明します。

PHPとアプリケーションを最新に保つ

PHPは、新しいリリースが出るたびに、PHPコミュニティーで報告されている脆弱性を排除するためのセキュリティ強化を図っています。リリースとリリースの間でZend社は、「ホット・フィックス」を発行します。「ホット・フィックス」とは、次のリリースが出るまでの間、セキュリティを危うくする可能性のある重大なバグを修正するための一時的なパッチのことです。
Zend Networkの自動更新は、セキュリティを維持するために必要なパッチやリリース更新を適用しますが、イスラエルのラマトガン市にあるZend社研究所のシニア・コンサルタントで、Zend's i5 Global Services部門のチーム・リーダーでもあるシュロモ・ヴァヌヌ氏は、このZend Networkの自動更新を構成することで最新の状態に保っておくよう、管理者に勧めています。ヴァヌヌ氏によれば、IBMはSystem iの全ユーザーに対して、Zend Network Silver Supportの無料購読を通じて上記の更新を入手できるように準備したとのことです。

自動更新を構成する手順は以下の通りです。

  1. Zend Networkに登録する(zend.com/network)。Zend CoreをWebからダウンロード済みであれば、既に登録されている場合があります。
  2. System iのコマンドラインから、以下のコマンドを入力する。
    GO ZENDCORE/ZCMENU
  3. メニュー・オプション2、Zend Networkから更新する、を選択する。
  4. 更新メニューから、すぐに更新またはスケジュール更新を選択する。

PHP独自の更新の他に、有名なPHPベースのWebアプリケーションが独自のパッチや定期的なリリース更新を発行しています。クリス・スナイダー氏によれば、非常にうまくいったプロジェクトでも危険にさらされるバグを残してしまうことがあるとのことですので、脆弱性とそれに対応した更新については、誰もが以下を介して知っておく必要があります。

  • ダニエル・コンビッサー氏が提供しているSecurityFocusのサマリ(phpsec.org/projects/vulnerabilities/securityfocus.html)。
  • 開発チームが提供している電子メール・ニュースレター
  • 開発者向けのニュース提供や開発者のためのコミュニティーであるWebベースのフォーラム

また、PHPの構成ファイルであるphp.iniについても言及しておいたほうがいいでしょう。PHPのグローバル設定は、本稿で紹介するセキュリティに関する値も含まれていますが、/usr/local/Zend/Core/etc/php.iniに保存されています。システムを保護するためには、このファイルへのアクセスを制御する必要があるのは言うまでもありません。つまり、このファイルに誰がアクセスできるのかを制限し、このファイルへの書き込み権限をWebアプリケーションには与えないということを確実にしておかなければならないということです。このファイルは、WRKLNKコマンドでWindowsベースのテキスト・エディタで編集できます。
php.iniファイルのフォーマットは以下の通り、通常の 変数=値、文法:設定=値 というフォーマットになっています。

error_log = /usr/local/Zend/Core/logs/php_error_log

2値の値(onまたはoff)のときは、次のような行になります。

setting = Onまたはsetting = Off

たとえば、次のようになります。

log_errors = On

注記: OnとOffの代わりに数字の1と0を使用することもできます。また、以下の手順で、WebベースのZend Coreコントロール・センターからphp.iniの設定をGUIで編集することもできます。

  1. http://hostname:8000/ZendCoreを表示させる。
  2. 構成メニューをクリックする。
  3. PHP構成オプションを選択する。

php.iniをどのような手段で編集するにしろ、ZendのApache Webサーバーを再起動しないと更新が有効になりません。Apacheの再起動は以下の手順で行います。

  1. GO ZENDCORE/ZCMENUと入力する。
  2. オプション5、サービス管理メニューを選択する。
  3. オプション6、Apacheサーバー・インスタンスの再起動を選択する。

初期化されていない変数に注意

PHPは効率を重視するため、変数を使用する前にそれが初期化されているか否かをチェックしません。プログラマが変数の初期化を忘れると、「意図しない結果」が発生する可能性があります。通常、初期化されていない変数は、アプリケーションの失敗の原因となることはあっても、セキュリティの問題につながることはありません。しかし、初期化されていない変数が起爆剤になってしまうことになるシナリオがあります。それはPHPのregister_globalsオプションです。

このオプションの設定は、URL中に含まれているqueryString引数から直接変数をセットできるか否かを制御します。register_globalsオプションのデフォルト値はOffとなっていて、URLから変数への値の割り当てができないようになっています。しかし、このオプションをOnに設定すると、URLに値の割り当てを含めることでどのユーザーもスクリプト変数を直接操作することができます。たとえば、スクリプトでユーザーが認証済みであることを表す、authenticationPassedという変数を使っている場合、悪意を持った者がURLに「authenticationPassed=1」という文字列を加えるだけで認証コードをすり抜けることができます。

こうした悪用に対する予防措置は当然のことながら、register_globalsの値がデフォルトのOffのままであることを確かめることです。register_globalsの脆弱性のその他の例については、php.net/register_globalsを参照してください。

プログラムのエラー・メッセージ

プログラムのエラー・メッセージがなかったらどういう状況になるでしょう。「郵便番号の入力は必須です」などといったエンド・ユーザー向けのエラー・メッセージのことではなく、「配列のインデックスが境界を越えています」などといったプログラム内部の問題に関するエラーのことです。

アプリケーションを開発しているときはこうしたメッセージは絶対必要です。にもかかわらず、システムがいったんエンド・ユーザーに導入されてしまったら、そうしたメッセージが出ると素人が作ったアプリケーションのように見えるだけでなく、ファイルの場所や内部APIのパラメーターなどといった機密情報を明かしてしまうことになります。さらに、検索エンジンがエラー・メッセージにインデックスをつけていたら、ハッカー好みのつけいる隙のあるエラーを検索可能にしてしまいます。このテクニックは「グーグル・ハッキング(Google hacking)」という名前で実際に広まっています。

以下のphp.iniの設定を行うことで、エラー・メッセージがログ・ファイル(点検可能な場所)中で増えていても、一般のユーザーからは見えないようにすることができます。

display_errors = Off
log_errors = On
error_log = /usr/local/Zend/
 Core/logs/php_error_log (
 the default)
error_reporting =
 E_ALL | E_STRICT

PHPは、E_ALL | E_STRICTが指定されていると、どんなに小さいエラーでも警告や注意(初期化されていない変数など)を含め、全てのエラーをログに記録します。エラー・レポートを確実に行うための詳細情報については、php.net/error_reportingを参照してください。

phpinfo()の隠蔽

phpinfo()関数(マニュアルのphp.net/phpinfo頁)は、php.iniの値、拡張モジュール名、バージョン番号、Apacheの変数など、全てのPHP設定の一覧を出力します(図1)。

この関数の最も一般的な使い方は、phpinfo.phpという名前の簡単なHTMLドキュメントを作って、その中に以下の簡単なPHPスニペットを記述するものです。

<?php
phpinfo();
?>

この短いコードはPHPの全ての.ini設定、コンパイル・オプション、インクルードされている拡張モジュール、その他ハッカーには見せたくない膨大な情報の一覧を含む、大きなHTML文書に拡大していきます。
phpinfo()は非常に重要なデバッグ・ツールではありますが、分散した場所にある情報を1つのページに集めると、ハッカーが簡単に見つけて弱点を探してしまいます。アプリケーションのトラブルシューティングのためにphpinfo()の出力に簡単にアクセスできるようにしておきたくなりますが、この関数へのアクセスはパスワードで保護するなどして注意しておくべきです。

PHPのバージョン5.2.1では、検索エンジンがphpinfoページのインデックスを作成しないように指示するキーワードを追加することで状況を改善し、グーグル・ハッキングの脅威を減らしました。それでもなお、phpinfo()を見ることのできる外部者は、あなたが見せたい以上の情報をその中に見つけています。ですから、phpinfo.phpの慣例はどんなことがあっても採用しないでください。ハッカーはどこかに生贄になるサイトがないか探すときに、まずこれを手がかりにするからです。

データの暗号化

セキュリティの専門家であるクリス・スナイダー氏は、可能であればいつでも暗号化(意図した受取人にデータが到達するまで完全性と機密性を保つための手段)を使用することを勧めています。

なぜ暗号化が必要なのでしょうか。ウェブ・サーバーが暗号化されていない(普通のテキスト)データを送信する場合、送信されたデータが送達先に届く前に、密かに見張られたり、改竄されたりする可能性があります。そうした被害は、企業内LAN (社員やスパイ・ウェアによる)、インターネット上、インターネット・カフェの無線信号からなど、いたるところで発生する可能性があります。

暗号化は、パスワード、クレジットカード情報、個人データ、機密データなど、極秘情報を送信する際には特に確実な方法です。

最も一般的な暗号化の方法は、セキュア・ソケット・レイヤー/トランスポート・レイヤー・セキュリティ(SSL/TLS)で、アプリケーション・サーバーとユーザーのブラウザ間で暗号化し、途中の経路でHTTPパケットを盗聴されても復号化できないようにするというものです。

ただしSSL/TLSは、「隠し」HTMLフォームのフィールド中にエンコードされているデータなど、サーバーによって送信されるデータをエンド・ユーザーが見えないようにするものではないという点に注意してください。ブラウザの「ソースの表示」コマンドを使用すれば、誰でもその中身を表示させることはできます。内部変数の安全性を確保したいのであれば、隠しHTMLフィールドではなくサーバーサイド・セッション管理を使用してください。

System iはSSL/TLS暗号化をサポートしています。ユーザーは、URLの先頭におなじみの「https://」がつくのでわかるでしょう。SSL/TLSの設定方法は、IBMのオンラインi5/OS InfoCenterを参照してください。

ファイルの分割

PHPベースのウェブ・サイトは、その複雑さに関係なく、外部ファイルと内部ファイルが混在している場合が多いようです。

ブラウザは、URLを介して外部ファイルにアクセスします。ユーザーはこのURLをブラウザに入力するか、またはリンクとしてクリックします。内部ファイルはアプリケーションが使用するファイル(そしておそらくユーザーに対しても表示されるもの)ですが、ブラウザから直接アクセスすることはできず、アプリケーションのコードからのみ取り出されるものです。内部ファイルにユーザーが直接アクセスできるようになっていると、危険性が生じます。

外部ファイルの例としては、index.php、スタイルシート、ウェブ・ブラウザがリクエストするその他のファイル(PHPファイルやそれ以外のファイル)があります。こうしたファイルは、Zendのデフォルトのrootである/www/zendcore/htdocsなどのドキュメントのrootに置いておいたほうが安全です。

内部ファイルの例としては、外部ファイル内で実行されるものの別ファイルとして格納されているコードに「インクルード」する部分などのように、アプリケーションが使用するファイルがあります。パスワードを含む構成ファイルも同様に、内部ファイルとなることがしばしばあります。こうした内部ファイルは、ドキュメントのroot (htdocs)とは別の、ユーザーが実行したり参照したりできないような場所に、格納しておくべきです。/www/zendcore/など、安全な場所からファイルをインクルード(実行)する例を、次に示します。

<?php
include (&apos;/www/zendcore/
 includes/config.inc.php&apos;);
// other code follows
?>

もう1つの危険なカテゴリとして、ユーザーがアップロードするもの全般があげられます。ユーザーが提供するファイルは決して、ドキュメントのrootに置いてはいけません。悪意を持ったユーザーが有害なスクリプトをアップロードして、あなたのサーバーで実行させようとしたらどうなるか、想像してみてください。アップロードしてくるファイルの配置場所としては、/www/zendcore/uploadsなどといったように、root以外のフォルダを選択してください。

さらなる保護策として、このディレクトリは書き込みのみ許可にしておき、自分のアプリケーションでさえもここに格納されているファイルを読んだり実行したりできないようにしてください。PHPではないバッチ・スクリプトを使用して、アップロードされたファイルを取り出してください。また、アップロードされたファイル中に間接的なディレクトリ作成リクエストが組み込まれようとしていたら、アプリケーションで注意深くそれらを取り除くようにしてください。たとえば、hackerslair/mygoodies/file1などといったファイル名は、アプリケーション側で検知し拒否するようにしておくべきです。

スクリプトに自由な支配権を与えない

PHPは、もし許可されていれば、ローカル・フォルダとインターネットの両方のコンテンツを読むことができます。たとえば、include()とreadfile()という関数は都合の良いことに、/reports/quarterly.xlsという名前のローカル・ファイル名も読めますし、リモートのURLにあるファイル名も読むことができます。

こうしたパワーを思い通りに使うことができるので、本当に必要なものは何であるか考え、必要のない機能は制限するのが賢明です。こうした慎重なアプローチによって、予期せぬ有害なスクリプトやネットワーク詐欺からさらに防護することができるのです。
php.iniにあるオプションでは、以下のようにスクリプトがアクセスできる資源を制限できます。

allow_url_include
(デフォルト値: Off; 推奨値: Off)。 この値をOffに設定しておくことで、リモートのサーバーに置かれているコードをアプリケーションが実行(「インクルード」)するのを禁止することができます。通常、include()やrequire()などといったコマンドは、ローカルのサーバーだけでなくリモートのサーバーからURLを介して、コードをインクルードすることができます。この値をOnに設定することで生じる危険性は、リモートにある攻撃者のスクリプトを実行してしまうことです。allow_url_includeは、PHP 5.2 (Zend Core 2.0)で導入されました。

allow_url_fopen
(デフォルト値: On、推奨値: 可能であればOff)。この値をOffに設定すると、allow_url_fopenはallow_url_includeよりも強い制限を受けることになり、アプリケーションはURLを介したリモートのファイルを読むことすらできなくなります。アプリケーションが他のURLをオープンする必要がなければ、もしくはリモートのURLをネイティブのコマンドよりも安全に読むことができるcURL拡張を代用できるのであれば、この値をOffにしてください。

open_basedir
(デフォルト値: 全てのファイルをオープンできる、推奨値: アプリケーションが必要とするフォルダ)。open_basedirにフォルダの一覧を指定すると、readfile()やfopen()などのコマンドは、指定されたフォルダ内のファイルだけにアクセスができます。その結果、スクリプトは機密情報を読んだり、他の場所にあるファイルを修正したりすることができなくなります。複数のフォルダを指定する場合は、コロン(Windowsシステムではセミコロン)で区切ってください。たとえば次の例は、/tmp、/www/zendcore/htdocs、/reportsというフォルダだけにアクセスできるように、アプリケーションに制限を加えます。

open_basedir = /tmp/:/www/zendcore/htdocs/:/reports/

PHPSecInfo

PHPセキュリティ・コンソーシアム(PHP Security Consortium)から無料で入手できるPHPSecInfoというツールは、構成を分析して可能性のあるセキュリティの落とし穴をレポートしてくれます(図2)。PHPSecInfoは実験的なツールです。その推奨事項は一般的なものであり、やみくもにそれらを適用すべきではありません。それでも参考にはなりますので、phpsec.org/projects/phpsecinfoからダウンロードしてみる価値はあるでしょう。

構成の保護パワー

PHPの構成に簡単な変更をいくつか加えるだけで、アプリケーションの誤用を防ぐか、あるいは大幅に抑えることができます。本稿で紹介したガイドラインは、ウェブ上の創造的あるいは革新的なアプリケーションに対する安全網の役割を提供するものです。より詳細について知りたい場合は、下記の「より詳細をお知りになりたい方」の一覧にある連絡先にご相談ください。

カスタム・アプリケーションは、セキュリティを考慮してこそ、良い設計といえます。本稿で紹介した、環境を安全にするためのテクニックを使用したら、次のステップは安全なコードを書くことです。次回は、入力のフィルタリング、出力のエスケーピングなどといった、SQL注入やクロス・サイト・スクリプティング(XSS)を無効にするテクニックについてご紹介します。

あわせて読みたい記事

PAGE TOP