Smartyのバグ ~default_modifiers~
- 2007/12/02
海原才人
わかりやすいネタのほうがソーシャルブックマーク数が稼げるからこういうネタを掲載するわけじゃないです。
そう、ぼくはただこの土日を使って社内のイケメンリストを作りたかったんです。
硬派なぼくはerror_reportingは常時E_ALLだし、クロスサイト・スクリプティング(以下XSS)の脆弱性を産むようなミスは犯したりしないゼ。
へなちょこでもいい。たくましいプログラミングをしたいんだ。
しかし悲劇は起こりました。
1 イケメン一覧スクリプトを作成する
まず、Asial社内のイケメンリストを配列に持ち、Smartyのassignメソッドで配列をテンプレート変数に割り当て。
また、XSSの脆弱性を未然に防ぐため、Smartyのdefault_modifiersにescapeをセットし、自動的にhtmlspecialchars関数がかかるように設定します。
ikemen_list.php
次に、テンプレート側のスクリプトを組みました。
ちゃんと値がassignされたかを確認するために、{debug}関数を挟みます。
ikemen_list.html
そして実行。
2 悲劇の始まり ~Smartyのバグ~

ナニィィィ!!?syntax error(構文エラー)だとォ?
どこを何度見ても構文を間違えている様子はないですよね。
仕方ないので
50^50A^50AAF14B%%ikemen_list.html.php
このコンパイル後の24行目で、何が起こっているのかを確かめてみることにします。
問題となっているのがこのくだり。↓
変数の中身を判定して、修飾子の処理を噛まそうとしてます。
このif文が構文エラーとなっているわけですが、要約すると
さらにもうちょっと解りやすく噛み砕くと、
コレが構文エラーの問題です。
phpマニュアルでisset関数を調べてみると、『Warning: isset() は何らかの値が渡された変数の場合のみ動作します。そうでない場合、パースエラーとなります。』とありました。isset関数で、式を評価してはいけませんね!
対策として、修飾子を実行させないよう
そして再実行
3 default_modifiersを設定していると{debug}もダメ

またsyntax error!?
どうせ先ほどと同じ問題だと思ったので、中身を見てみると案の定。
しかし、この問題を回避するのは難しそうです。
default_modifiersをセットすると、{debug}関数が使えないことが判明しました。
解決方法があったら、どなたか教えてください。
気を取り直し、{debug}を取り除いて再実行
4 {foreach}する配列もdefault_modifiersの対象となる

どうやら今度は、escape修飾子関数内で問題が起こっているようでした。
プラグインの中の
modifier.escape.php
を調べてみることにしました。
詳しくコンパイルファイルを調べてみると、やはりdefault_modifiersが悪さをしていました。
ループしたい変数が配列の場合、_run_mod_handlerというメソッド内で配列の中身をもすべて分解してdefault_modifire処理をかけようとします。
しかし_run_mod_handlerメソッド内では、1次元配列までにしか対応していませんでした。だから、今回のように2次元配列である$ikemen_listをループする場合、1次元配列にescape修飾子処理をかけようとしてWarningが出ちゃうんです。
かなり付け焼刃的な措置ではありますが、配列が渡ってきた場合は無視するよう追記します。
本当なら、多次元配列に対応できるように修正すべきなんだけど、めんどうなので「あとでやる」。
(ここはむしろ新ジャンル「だれかやって」)
ということで、改めて実行

ぃよっしゃー!
5 まとめ
・$default_modifiersを設定すると、{debug}が使えない
対策→わからないのでだれか教えてください
・$default_modifiersを設定すると、2次元配列を{foreach}できない
対策→plugin/modifier.escape.phpを配列が渡ってきても大丈夫なように改造する
・$default_modifiersを設定すると、{if isset($variable)}のように、if文内でisset関数が使えない
対策→{if isset($variable|smarty:nodefaults)}としてdefault_modifiersを無効にする
そう、ぼくはただこの土日を使って社内のイケメンリストを作りたかったんです。
硬派なぼくはerror_reportingは常時E_ALLだし、クロスサイト・スクリプティング(以下XSS)の脆弱性を産むようなミスは犯したりしないゼ。
へなちょこでもいい。たくましいプログラミングをしたいんだ。
しかし悲劇は起こりました。
1 イケメン一覧スクリプトを作成する
まず、Asial社内のイケメンリストを配列に持ち、Smartyのassignメソッドで配列をテンプレート変数に割り当て。
また、XSSの脆弱性を未然に防ぐため、Smartyのdefault_modifiersにescapeをセットし、自動的にhtmlspecialchars関数がかかるように設定します。
ikemen_list.php
- //
イケメンリストの配列を作成 - $ikemen_list[0]["name"]
"海原";= - $ikemen_list[0]["nickname"]
"saity";= - $ikemen_list[0]["honey_flg"]
"0";= - $ikemen_list[1]["name"]
"田中";= - $ikemen_list[1]["nickname"]
"massie";= - $ikemen_list[1]["honey_flg"]
"1";= - $ikemen_list[2]["name"]
"森川";= - $ikemen_list[2]["nickname"]
"joe";= - $ikemen_list[2]["honey_flg"]
"1";= - $ikemen_list[3]["name"]
"松田";= - $ikemen_list[3]["nickname"]
"mattsun";= - $ikemen_list[3]["honey_flg"]
"0";= - require_once("../libs/Smarty.class.php");
- $smarty
= new Smarty(); - $smarty->template_dir
= "/home/saito/templates"; - $smarty->compile_dir
"/home/saito/templates_c";= - $smarty->default_modifiers
= array('escape'); - $smarty->assign("ikemen_list",$ikemen_list);
- $smarty->display("ikemen_list.html");
次に、テンプレート側のスクリプトを組みました。
ちゃんと値がassignされたかを確認するために、{debug}関数を挟みます。
ikemen_list.html
- <html>
- <head>
- <title>社内モテリスト</title>
- <body>
- {debug}
- <h1>アシアルのイケメン一覧表</h1>
- {foreach
from=$ikemen_list item=ikemen name="motefoobar"} {$smarty.foreach.motefoobar.iteration}: {$ikemen.name}: {$ikemen.nickname}: {if isset($ikemen.honey_flg) && $ikemen.honey_flg}彼女有{else}彼女無{/if} <br>- {/foreach}
そして実行。
2 悲劇の始まり ~Smartyのバグ~
ナニィィィ!!?syntax error(構文エラー)だとォ?
どこを何度見ても構文を間違えている様子はないですよね。
仕方ないので
50^50A^50AAF14B%%ikemen_list.html.php
このコンパイル後の24行目で、何が起こっているのかを確かめてみることにします。
問題となっているのがこのくだり。↓
if (isset ( ((is_array($_tmp=$this->_tpl_vars['ikemen']['honey_flg']))? $this->_run_mod_handler('escape', true, $_tmp): smarty_modifier_escape($_tmp))| )&& ((is_array($_tmp=$this->_tpl_vars['ikemen']['honey_flg']))? $this->_run_mod_handler('escape', true, $_tmp): smarty_modifier_escape($_tmp))): 彼女有 else: 彼女無 endif;
変数の中身を判定して、修飾子の処理を噛まそうとしてます。
このif文が構文エラーとなっているわけですが、要約すると
- if
(isset((is_array(($a) ? $b : $c)))) {
さらにもうちょっと解りやすく噛み砕くと、
- if
(isset(($a = $b))){
コレが構文エラーの問題です。
phpマニュアルでisset関数を調べてみると、『Warning: isset() は何らかの値が渡された変数の場合のみ動作します。そうでない場合、パースエラーとなります。』とありました。isset関数で、式を評価してはいけませんね!
対策として、修飾子を実行させないよう
- {if
isset($ikemen.honey_flg|smarty:nodefaults) && $ikemen.honey_flg}彼女有{else}彼女無{/if}
そして再実行
3 default_modifiersを設定していると{debug}もダメ
またsyntax error!?
どうせ先ほどと同じ問題だと思ったので、中身を見てみると案の定。
しかし、この問題を回避するのは難しそうです。
default_modifiersをセットすると、{debug}関数が使えないことが判明しました。
解決方法があったら、どなたか教えてください。
気を取り直し、{debug}を取り除いて再実行
4 {foreach}する配列もdefault_modifiersの対象となる
どうやら今度は、escape修飾子関数内で問題が起こっているようでした。
プラグインの中の
modifier.escape.php
を調べてみることにしました。
- function
smarty_modifier_escape($string, $esc_type = 'html', $char_set = 'ISO-8859-1') - {
switch ($esc_type) { case 'html': return htmlspecialchars($string, ENT_QUOTES, $char_set); - 以下略
詳しくコンパイルファイルを調べてみると、やはりdefault_modifiersが悪さをしていました。
ループしたい変数が配列の場合、_run_mod_handlerというメソッド内で配列の中身をもすべて分解してdefault_modifire処理をかけようとします。
しかし_run_mod_handlerメソッド内では、1次元配列までにしか対応していませんでした。だから、今回のように2次元配列である$ikemen_listをループする場合、1次元配列にescape修飾子処理をかけようとしてWarningが出ちゃうんです。
かなり付け焼刃的な措置ではありますが、配列が渡ってきた場合は無視するよう追記します。
- function
smarty_modifier_escape($string, $esc_type = 'html', $char_set = 'ISO-8859-1') - {
// 配列が渡ってきたら何もせずにreturn if (is_array($string)) return $string; switch ($esc_type) { case 'html': return htmlspecialchars($string, ENT_QUOTES, $char_set); - 以下略
本当なら、多次元配列に対応できるように修正すべきなんだけど、めんどうなので「あとでやる」。
(ここはむしろ新ジャンル「だれかやって」)
ということで、改めて実行
ぃよっしゃー!
5 まとめ
・$default_modifiersを設定すると、{debug}が使えない
対策→わからないのでだれか教えてください
・$default_modifiersを設定すると、2次元配列を{foreach}できない
対策→plugin/modifier.escape.phpを配列が渡ってきても大丈夫なように改造する
・$default_modifiersを設定すると、{if isset($variable)}のように、if文内でisset関数が使えない
対策→{if isset($variable|smarty:nodefaults)}としてdefault_modifiersを無効にする
コメント
コメントフォーム
トラックバック
-
- [PHP]Smartyで自動HTMLエスケープ from 浅く広くをモットーに - WEBプログラマ メモ
- アシアルブログ - Smartyのバグ 〜default_modifiers〜 バグはバグなのかもし...
2007年12月04日 13:51
-
- [PHP]Smarty の default_modifiers のバグ from ひろのぶの日記
- いつも楽しく読ませてもらっているアシアル株式会社さんのブログから...
2007年12月04日 17:38
-
- assign時にデフォルトでhtmlspecialcharsを行うようにする from MOVION.net
- 個人的にお遊びサイトを開発中(ポンティ)なのですが、 ふとタグを入...
2008年02月11日 02:58
最近の記事
- もうすぐ健康診断があるんだ・・・ [2010年09月02日 : 阿部恵]
- Photoshopで壁紙を作りながら、基本的な使い方を覚える [2010年09月01日 : 鴨田健次]
- はじめての共同作業 Canvas編 (node.js + websocket) [2010年09月01日 : 中川善樹]
- 「PHP×Flex(後編)」PHPテクニカルセミナー(無料)第4弾の募集を開始しました!! [2010年08月26日 : 和田記光]
- 【HTML5】Canvasでお絵かきしてみた(前編) [2010年08月25日 : 橋本章史]
- MacにgroongaのMySQL用ストレージエンジン [2010年08月23日 : 笹亀弘]
- Appleのサイトで見たiPhone4をFireworksで描いてみました-1/2 [2010年08月19日 : 和田記光]
- iPad版の会社紹介を作ってみました [2010年08月19日 : 小林有佳]
- iPhoneアプリ開発開始時に気をつけるべきファイルの取り扱い (2) [2010年08月19日 : 亀本大地]
- symfonyセミナー動画無料公開! [2010年08月13日 : 岡本雄樹]



$ikemen_list[4]["name"] = "かめもと";
$ikemen_list[4]["nickname"] = "yudoufu";
$ikemen_list[4]["honey_flg"] = "0";
これが足りないせいじゃないかな(`・ω・´)
$ikemen_list[5]["name"] = "笹亀";
$ikemen_list[5]["nickname"] = "sasa";
$ikemen_list[5]["honey_flg"] = "1";
きっとこれも足りないから動かない(;゚Д゚)
もっと大胆に行かなきゃダメか。
http://nonn-et-twk.net/twk/node/133
↑この人に、バグ報告のメールしておきましたよん。
どちらの山田さんでしょう?僕の生徒さん?本をいっぱい出版されている??
気になる気になるとき気になれば気になろう…。
いえいえ。でも音沙汰がありません・・・。
> どちらの山田さんでしょう?
PHP暦半年のSaityさんの知らない山田です。
でも僕の先生はSaityさんを知ってるそうです。
(こういうこと書くと、よけいに気になりますね。)
ところで、今の案件で、Smartyのdefault_modifiers使ってます。
痛いバグは、Saityさんのバグ措置をコペりました。
もう、20機能ほど実装しましたが、問題なく動いています。
このdefault_modifiers、滅茶苦茶便利じゃないですか!?
なぜこんな便利な機能があるのに、Smartyチームは
これを前面に押し出さないんでしょう。
たった一行の設定で、わずらわしいXSS対策作業が
ほぼ完了しますよね。
僕がSmartyチームの一員だったら、名刺に
「default_modifiersのSmarty!」と印刷しておきたいくらいです。
最近は、フレームワーク/VIEWはPHPがはやってきていますが、
PHPのコードをPHPでパースすることは大変なので、このような
ことってできないですよね?(多分)
symfonyならできるんですね。失礼しました。
CakePHPにh()なんてものがあるので無理かと思ってました。。。