Smartyをちょこっとだけ触ってみる

ほとんど忘れていたPHP5の復習がてら,Smarty触ってみた.

とりあえずサンプルコードから適当にコピペしてでっちあげる.動く.全く簡単だ(動かす分には).

メモ

  • {$var|escape:"htmlall":char_set}はhtmlentities($string, ENT_QUOTES, $char_set)相当
  • default_modifiersに書くと全項目で適用されるescapeを書けるけど,charsetを忘れずに指定しておく
    • さもないとXSS脆弱性になる可能性あり
      • 文字列の末尾にマルチバイト文字の1byte目っぽいコードを食わせてvalue="foo#" (#が0x81とか0xC0)とかして「"」をマルチバイト文字の一部だとWebブラウザに誤認させてマークアップを崩すアレ
    • escapeしたくない項目は,個別に{$var|smarty:nodefaults}とかすれば良し
    • ググッて見つかるようなSmartyのサンプルでは,なぜかdefault_modifiers=array('escape:"htmlall"')とか,htmlentitiesの第三引数(charset)書いてない場合が多い.何故だ.みんなノーガード戦法か?
  • テンプレート中で,meta〜charsetを何よりも先に書く.またはApacheの方でAddDefaultCharset: UTF-8とかしてHTTPレスポンスヘッダでcharset吐く(UTF-7 XSS対策)
    • PHPに限らないけど
  • 属性値をクォートしない奴はクビ
    • PHPに限らないけど
    • テンプレート例)
    • フォーム入力例)
      • `` style=`background-image: url(javascript:alert(document.URL))`
    • HTML出力例)
      • <input value=`` style=`background-image: url(javascript:alert(document.URL))`>
    • IE6は,バッククォートで属性値をクォートできるという素敵仕様のため,こんな感じでhtmlentitiesに引っかからない文字列だけでXSSできる(クォート忘れてると)
    • その他XSSの例: http://ha.ckers.org/xss

ToDo

  • templateはDocumentRootの外に置くなり403とかになるようにしとけ > 俺
  • Smarty自体もDocumentRootの外に置いてphp.iniでinclude_path通しとけ > 俺

ソース

index.php
<?php
require_once('smarty/Smarty.class.php');
$smarty = new Smarty;
$smarty->default_modifiers = array('escape:"htmlall":"UTF-8"');

$name = isset($_POST['name'])
          ? $_POST['name']
          : 'id:ihag';
$url  = isset($_POST['url'])
          ? $_POST['url']
          : 'http://d.hatena.ne.jp/ihag/';

$smarty->assign('name', $name);
$smarty->assign('url', $url);

$smarty->display('index.tpl');
?>
template/index.tpl
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>Index page</title>
</head>

<body>
  <p>ユーザ情報表示:</p>

  <form method="POST" action="/index.php">
    名前: <input type="text" size="60" name="name" value="{$name}"><br>
    URL: <input type="text" size="80" name="url" value="{$url}"><br>
    <input type="submit" value="Submit">
  </form>
</body>
</html>