予告:4/
機能的な問題以外ににも、
- 日本語が使えない
- エスケープのセキュリティ対策が十分でない
- ZendFrameworkらしくないViewでのエスケープ
しかし、
ZendFrameworkを使ったほうが、
データベースの設定修正
まずはデータベースの設定を修正します。データベースには2つの問題があります。
- 文字エンコーディング
- ユーザアカウント
文字エンコーディングの問題
どんなアプリケーションでも利用する文字エンコーディングを正しく取り扱わないと、
今回はPostgreSQLをデータベースサーバとして利用しています。PostgreSQLはMySQLやMS SQL Server、
PostgreSQLサーバのインストールや設定によっては、
[framework@localhost www]$ psql -U postgres -l List of databases Name | Owner | Encoding -----------+---------------+---------- guestbook | postgres | UTF8 mediawiki | mediawikiuser | UTF8 postgres | postgres | UTF8 template0 | postgres | UTF8 template1 | postgres | UTF8 (5 rows)
[yohgaki@dev $ psql -U postgres -l Password for user postgres: List of databases Name | Owner | Encoding -----------+---------------+----------- guestbook | postgres | SQL_ASCII mediawiki | mediawikiuser | SQL_ASCII postgres | postgres | SQL_ASCII template0 | postgres | SQL_ASCII template1 | postgres | SQL_ASCII (5 rows)
PostgreSQLのSQL_
もし、
ユーザアカウントの問題
データベースユーザが特権ユーザである
通常、
データベースの問題を修正
PostgreSQLの場合、
$ dropdb guestbook
ゲストブックデータベースにアクセスする新しいデータベースユーザを作ります。
$ /opt/PostgreSQL/8.3/bin/createuser -U postgres -W zfguestbook Shall the new role be a superuser? (y/n) n Shall the new role be allowed to create databases? (y/n) n Shall the new role be allowed to create more new roles? (y/n) n Password:zfguestbook(※実際には表示されません)
通常ユーザ、
$ createdb -U postgres -O zfguestbook -E utf8 guestbook
[yohgaki@dev PHP-ZendFramework]$ psql -l -U postgres Password for user postgres: List of databases Name | Owner | Encoding -----------+---------------+----------- guestbook | zfguestbook | UTF8 mediawiki | mediawikiuser | SQL_ASCII postgres | postgres | SQL_ASCII template0 | postgres | SQL_ASCII template1 | postgres | SQL_ASCII (5 rows)
psqlコマンドでデータベースを一覧すると新しいオーナーとエンコーディングでデータベースが作成されていることが確認できます。
$ psql -U zfguestbook -f /www/guestbook/guestbook.sql guestbook
$ psql -U zfguestbook guestbook Password for user zfguestbook: Welcome to psql 8.3.5, the PostgreSQL interactive terminal. Type: \copyright for distribution terms \h for help with SQL commands \? for help with psql commands \g or terminate with semicolon to execute query \q to quit guestbook=> \d List of relations Schema | Name | Type | Owner --------+------------------+----------+------------- public | guestbook | table | zfguestbook public | guestbook_id_seq | sequence | zfguestbook (2 rows)
アプリケーションの修正
アプリケーションも修正しなければなりません
今回、
通常版guestbookの修正
問題となる部分は
- 文字エンコーディングの処理
- 文字列のエスケープ処理
です。
前回作ったゲストブックアプリケーションでは、
環境によってはページの表示さえ文字化けしてしまいます。
![図1 ページ表示の文字化け 図1 ページ表示の文字化け](/assets/images/dev/serial/01/zf-ajax/0004/thumb/TH800_001.png)
この環境ではWebサーバがUTF-8エンコーディングのページをShift_
![図2 ポスト完了後のページの文字化け 図2 ポスト完了後のページの文字化け](/assets/images/dev/serial/01/zf-ajax/0004/thumb/TH800_002.png)
ブラウザの文字エンコーディングの自動認識を有効にしてUTF-8で記述されたページであることを認識させるだけでは不十分です
![図3 自動認識を有効にしてpostした場合 図3 自動認識を有効にしてpostした場合](/assets/images/dev/serial/01/zf-ajax/0004/thumb/TH400_003.png)
文字エンコーディングの自動認識を有効にしても、
![図4 リスト表示の文字化け 図4 リスト表示の文字化け](/assets/images/dev/serial/01/zf-ajax/0004/thumb/TH400_004.png)
このような文字化けが発生する原因は、
PHPで日本語アプリケーションを作る場合、
// 文字エンコーディングを設定する
ini_set('default_charset', 'UTF-8');
mb_internal_encoding('UTF-8');
これらの設定はphp.
これだけでかなり動作がマシになりますが、
日時: <?php echo htmlentities($row['date_created'], ENT_QUOTES, 'UTF-8') ?><br />
デフォルトではENT_
これらの修正を行った通常版ゲストブックアプリケーションは、
![図5 日本語が正しく表示されている状態 図5 日本語が正しく表示されている状態](/assets/images/dev/serial/01/zf-ajax/0004/thumb/TH400_005.png)
ドキュメント以外のスクリプト
guestbook2ではinit.
ドキュメントルートにアクセスする必要がないファイルは配置しないようにすることが重要です。もし、
<?php
if (basename($_SERVER['SCRIPT_FILENAME']) == basename(__FILE__)) {
trigger_error(__FILE__.' is called directly by '.$_SERVER['REMOTE_ADDR']);
die('Cannot call directly');
}
![図6 アクセスエラー 図6 アクセスエラー](/assets/images/dev/serial/01/zf-ajax/0004/thumb/TH800_006.png)
パス情報は攻撃者が攻撃を成功させるための重要な情報となります。この画面ではエラーメッセージが表示されていますが、
ZendFramework版guestbookの修正
ZendFramework版guestbookも通常版と同様の変更を行う必要がありますが、
解説を進める前に、
![図7 データベースサーバ側の文字エンコーディングチェックによるエラー 図7 データベースサーバ側の文字エンコーディングチェックによるエラー](/assets/images/dev/serial/01/zf-ajax/0004/thumb/TH800_007.png)
これはUTF-8エンコーディングのデータベースに対して、
実はWebアプリケーションもPostgreSQLサーバと同様に入力文字エンコーディングが正しいかチェックしなければなりません。不正な文字エンコーディングがさまざまな攻撃に利用可能だからです。本来このチェックは必要ですが、
ZendFrameworkのViewオブジェクトには文字エンコーディング設定がありますが、
$this->view->setEncoding('UTF-8');
$this->view->setEscape('htmlentities');
ini_set('default_charset','UTF-8');
mb_internal_encoding('UTF-8');
前のバージョンのguestbookではZendFrameworkアプリケーションらしく、
件名: <?php echo $this->escape($_POST['title']) ?><br />
このescapeメソッドを利用すればよいのですが、
/**
* Escapes a value for output in a view script.
*
* If escaping mechanism is one of htmlspecialchars or htmlentities, uses
* {@link $_encoding} setting.
*
* @param mixed $var The output to escape.
* @return mixed The escaped value.
*/
public function escape($var)
{
if (in_array($this->_escape, array('htmlspecialchars', 'htmlentities'))) {
return call_user_func($this->_escape, $var, ENT_COMPAT, $this->_encoding);
}
return call_user_func($this->_escape, $var);
}
$this->_escapeのデフォルトは
ここで気づいた方も居ると思います。Zend Frameworkのエスケープメソッドでは、
フレームワークはベストプラクティスを集めている、
エスケープモードは設定できるようになっていませんが、
パフォーマンスの比較
Webアプリケーションフレームワークを利用するメリットは開発の効率化とメンテナンス性の向上です。以降では通常版のPHPアプリケーションを作成しないので、
データベースを利用したアプリケーションではデータベースがパフォーマンスのボトルネックになります。少しでもデータベースの影響を少なくするために、
shared_buffers = 512MB
fsync = off
上記の設定変更を行った後、
ベンチマークにはabコマンドを使用します。クライアントからはzendframeworkをホスト名としてサーバにアクセスできるようにサーバとクライアントを設定しています。
- コマンド
ab -c 50 -n 10000 http://
zendframework/ guestbook/ list/ ab -c 50 -n 10000 http:// zendframework/ list/ - サーバ
Core2Quad Q6600/
8GB RAM/ SATA-II 1TB - クライアント
Core2Duo 2.
1Ghz/ 4GB RAM
サーバとクライアントは1Gbpsのイーサネットで接続されています。
- 通常版
Requests per second: 471.
69 [#/sec] (mean) - ZendFramework版
Requests per second: 207.
58 [#/sec] (mean)
当然ですが通常版のほうがシンプルであるため倍以上高速です。データベースを利用していないアプリケーションであれば、
例えば、
- 通常版
Requests per second: 7014.
77 [#/sec] (mean) - ZendFramework版
Requests per second: 587.
34 [#/sec] (mean)
実行しているコード数がまったく異なるので10倍程度の違いは当然です。パフォーマンス的にはかなりのハンデのように思えるかも知れませんが、
guestbookとguestbook2の差分
UNIX系のシステムではテキストファイルの差分を抽出するdiffと呼ばれる便利なツールがあります。Windows用のdiffツールも数多く作られています。ソースコード管理システムのCVS, Subversion, Mercurial, Git等もdiff形式の差分ファイルをサポートしています。この連載ではMercurialを利用する予定ですが、
diffコマンド
UNIX/
- 使用法
- diff [オプション]... FILES
$ diff -u -r -b --new-file /www/guestbook /www/guestbook2 | less
等とすると差分が参照できます。
オプション | 動作 |
---|---|
-u | ユニバーサル形式の差分ファイルを生成 |
-r | 再帰的にファイルを比較 |
-b | 空白文字の違いを無視 |
--new--file | 新しいファイルも差分として表示 |
diff -ur -b --new-file /www/guestbook/application/views/scripts/list/list.phtml /www/guestbook2/application/views/scripts/list/list.phtml
--- /www/guestbook/application/views/scripts/list/list.phtml 2009-04-15 05:38:04.000000000 +0900
+++ /www/guestbook2/application/views/scripts/list/list.phtml 2009-04-18 05:42:36.000000000 +0900
@@ -10,9 +10,9 @@
<table>
<?php foreach ($this->rows as $row): ?>
<tr><td>
- 日時: <?php echo htmlentities($row['date_created']) ?><br />
- 件名:<?php echo htmlentities($row['title']) ?> <br />
- メッセージ: <?php echo htmlentities($row['content']) ?><br />
+ 日時: <?php echo $this->escape($row['date_created']) ?><br />
+ 件名:<?php echo $this->escape($row['title']) ?> <br />
+ メッセージ: <?php echo $this->escape($row['content']) ?><br />
</td>
</tr>
<?php endforeach; ?>
差分ファイルを初めて見る方でも、
この差分情報でどのような修正が行われたか分かります。diffコマンドで抽出した差分情報はpatchコマンドで追加したり、
guestbookとguestbook2の差分は以下からダウンロードできます。
- guestbookとguestbook2の差分
- guestbook2.
diff - guestbook2アプリ
- guestbook2-20090430.
tar. gz
まとめ
今回は前回作った素朴なゲストブックアプリのセキュリティ上の問題等を修正しました。
- 文字エンコーディングは明示的に指定し、
アプリケーション内で統一する - エスケープ方法は可能なかぎり安全性が最も高い方法を利用する
Zend Viewのescapeメソッドの実装も紹介しました。Zend Frameworkはオブジェクト指向設計を採用しているので、