第32回 PHPセキュリティ月間
今回もMOPS関連の話題です。MOPSではPHP関連のセキュリティ製品やセキュリティ知識の論文を募集し、
- MOPS Submission 02 – Context-aware HTML escaping
- http://
www. php-security. org/ 2010/ 05/ 05/ mops-submission-02-context-aware-html-escaping/ index. html
このテンプレートエンジンはNette Latteと呼ばれています。このテンプレートエンジンを独立して使用することもできますが、
コピーライトを見ると2004年からとなっており、
コンテクストを検出するHTMLテンプレートエンジンとは?
JavaScriptインジェクションを防ぐようHTMLを記述することは簡単ではありません。HTMLの中に記述される情報はテキストやHTMLでマークアップされたテキストだけではありません。タグの属性、
変数を記載する場所を自動的に検出してエスケープするテンプレートエンジンは
Nette Latteが検出するコンテクスト
Nette Latteは以下のコンテクストを検出して適切なエスケープ処理を行います。
- HTMLテキスト
- "または'で囲まれたHTML属性
- <script>または<style>で囲まれたCDATAのセクション
- HTML属性のstyleおよびonclickなどのイベンドハンドラ
- HTMLコメント
URLを属性値とする属性
Nette Latteの利用例
どのように利用するのかは利用例を見るとすぐ分かります。
<script type="text/javascript">
var userId = {$userId};
</script>;
<p style="color: {$color};" title="{$title}">;
<a href="" onclick="return !confirm({$message});">{$desc}</a>;
</p>
<!-- Executed in: {$time} s -->;
Nette Latteのテンプレートでは各変数をそのまま出力しているように記述しますが、
Nette Latteには自動的なエスケープを停止するオプションはありません。その代わりに {!$var} と変数の前に ! を追加します。
Nette Latteのマクロ
ほかのテンプレートエンジンと同様にさまざまなマクロをサポートしています。
{$variable} | テンプレート変数の出力 |
{!$variable} | テンプレート変数をエスケープなしで出力 |
{*text comment*} | テンプレート用のコメント |
{plink ...} | presenterのリンク |
{link ...} | urlの生成 |
{if ?} ... {elseif ?} ... {/if} | <?php if (?): ?> ... <?php elseif (?): ?> ... <?php endif ?> |
{foreach ?} ... {/foreach} | <?php foreach (?): ?> ... <?php endforeach ?> |
{for ?} ... {/for} | <?php for (?): ?> ... <?php endfor ?> |
{while ?} ... {/while} | <?php while (?): ?> ... <?php endwhile ?> |
{include dir/ | テンプレートのインクルード |
{var foo => value} | テンプレート変数宣言 |
{default foo => value} | デフォルト値の設定 |
{control loginForm} | ログインフォーム |
{dump $variable} | 変数のダンプ |
これだけでも十分な機能を持っていますが、
{=expression} | <?php echo htmlSpecialChars(expression) ?> |
{!=expression} | <?php echo expression ?> |
{?expression} | PHPコードを評価 |
{_expression} | トランスレーション |
{!_expression} | エスケープ無しのトランスレーション |
{ifCurrent} | {if}がアクティブリンクの場合の特別なif |
{include 'dir/ | テンプレートの読み込み |
{cache ?} ... {/cache} | キャッシュチェック |
{snippet ?} ... {/snippet} | 制御スニペット |
{attr ?} | HTMLタグ属性の登録 |
{capture $var} ... {/capture} | 出力バッファリングを行い$varに保存 |
{block | texy} ... {/block} | texy ブロック |
{widget ...} | コンポーネントをレンダリング |
{control ...} | widgetのエイリアス |
{contentType ?} | HTTPヘッダのContent-Typeを送信 |
{debugbreak} | ブレークポイントの挿入 |
Nette Latteの試用
Nete Latteが含まれているNette Frameworkは3つバージョンが用意されています。
- PHP 5.
2用でクラス名にプレフィックス有り - PHP 5.
2用でクラス名にプレフィックス無し - PHP 5.
3用で名前空間を使用
執筆時点での最新版は1.
Nette Latte テンプレートを使ってみる
MOPSの文書ではNette Latteテンプレートは独立して利用できると記述されていましたが、
1 <?php
2 require_once __DIR__ . '/../../Nette/loader.php';
3
4 use Nette\Forms\Form, Nette\Debug, Nette\Templates, Nette\Templates\Filters;
5
6 Debug::enable();
7
8 // absolute filesystem path to the web root
9 const WWW_DIR=__DIR__;
10
11 // absolute filesystem path to the application root
12 const APP_DIR=__DIR__;
13
14 // absolute filesystem path to the libraries
15 define('LIBS_DIR', __DIR__ . '/../../3rdParty');
16
17 // テンプレートオブジェクトの作成
18 $template = new Nette\Templates\Template;
19 // Nette Latteフィルタの登録
20 $template->registerFilter(new Nette\Templates\LatteFilter);
21 $template->setFile( 'template.phtml' );
22 // テンプレート変数の登録
23 $template->userId = 1234;
24 $template->arrayVar = array('a'=>1, 'b'=>1, 'c'=>3);
25 $template->stringVar = 'JS string <>"\'%';
26 $template->color = 'expression(); red"';
27 $template->title = 'ABC';
28 $template->url = 'http://www.es-i.jp/';
29 $template->message = 'Are you sure? "<>%';
30 $template->desc = '<Click Here>';
31 $template->time = '1234 -->';
32 // テンプレートを出力
33 echo $template;
34
テンプレートファイルは先程のサンプルテンプレートに多少付け加えたものを用意しました。
1 <html>
2 <head>
3 </head>
4 <body>
5 <script type="text/javascript">
6 var userId = {$userId};
7 var arrayVar = {$arrayVar};
8 var stringVar = {$stringVar};
9 </script>;
10 <p style="color: {$color};" title="{$title}">;
11 <a href="{$url}" onclick="return !confirm({$message});">{$desc}</a>;
12 </p>
13 <!-- Executed in: {$time} s -->;
14 </body>
Nette Latteのマクロとは{macro名}のように{}で囲まれた部分がマクロとしてテンプレートエンジンで置換されます。
{link}マクロと使おうとしたところエラーが発生したため、
{link コントローラ名:アクション名, パラメータ}
となっているので当然と言えば当然です。フレームワークを使っていない場合、
<html>
<head>
</head>
<body>
<script type="text/javascript">
var userId = 1234;
var arrayVar = {"a":1,"b":1,"c":3};
var stringVar = "JS string <>\"'%";
</script>;
<p style="color: expression\(\)\;\ red\";" title="ABC">;
<a href="http://www.es-i.jp/" onclick="return !confirm("Are you sure? \"<>%");"><Click Here></a>;
</p>
<!-- Executed in: 1234 --><!--> s -->;
</body>
いくつかエスケープすべき文字を入れてみましたが、
まとめ
Nette Latteはデフォルトでコンテクストに合わせたスケープ処理を行い、
ほかのフレームワークと組み合わせて使用する場合、