-
Notifications
You must be signed in to change notification settings - Fork 1
Text::Xslate::Manual::Cookbook.ja
Text::Xslate::Manual::Cookbookの日本語訳です。
Text::Xslateのバージョン1.5007から訳出しました。
Text::Xslate::Manual::Cookbook - Xslateテンプレートの料理法
このXslateクックブックは、Xslateの機能を紹介するレシピです。
HTMLフォームの管理は、ウェブアプリケーションにおける重要な関心事です。 テンプレートを用いて自分自身でどうにかするよりも、HTMLフォームを管理するためのモジュールを使うとよいでしょう。このセクションでは、FillInFormとHTMLフォームビルダーという、二つの基本的な解決法を提案します。
いずれの解決法も、 セキュリティーホール を作り込みやすい mark_rawフィルター
をテンプレート内で使うべきではありません。
その代わりに、 Text::Xslate
からエクスポートできる mark_raw関数
をアプリケーションコードで呼び出すことを考慮すべきです。
HTMLフォームを管理するための一つの解決法は、ブロックフィルター文法を用いて、FillInFormモジュールを使うことです。
HTML::FillInForm
を使ったコードの例です:
#!perl -w
use strict;
use Text::Xslate qw(html_builder);
use HTML::FillInForm; # HTML::FillInForm::Liteでもよいです
sub fillinform {
my($q) = @_;
my $fif = HTML::FillInForm->new();
return html_builder {
my($html) = @_;
return $fif->fill(\$html, $q);
};
}
my $tx = Text::Xslate->new(
function => {
fillinform => \&fillinform,
},
);
my %vars = (
q => { foo => "<埋め込まれた値>" },
);
print $tx->render_string(<<'T', \%vars);
FillInForm:
: block form | fillinform($q) -> {
<form>
<input type="text" name="foo" />
</form>
: }
T
出力はこうなります:
FillInForm:
<form>
<input type="text" name="foo" value="<埋め込まれた値>" />
</form>
HTML::FillInForm::Liteは fillinform
関数を提供しているので、さらに簡素にすることができます:
use HTML::FillInForm qw(fillinform);
my $tx = Text::Xslate->new(
function => { fillinform => html_builder(\&fillinform) },
);
詳細については、HTML::FillInFormやHTML::FillInForm::Liteもご覧ください。
HTMLフォームを管理するためのもう一つの解決法は、フォームビルダーを使うことです。
この場合、HTMLパーツで mark_raw()
を適用するだけで済みます。
HTML::Shakan
を使ったPSGIアプリケーションの例です:
#!psgi
use strict;
use warnings;
use Text::Xslate qw(mark_raw);
use HTML::Shakan;
use Plack::Request;
my $tx = Text::Xslate->new();
sub app {
my($env) = @_;
my $req = Plack::Request->new($env);
my $shakan = HTML::Shakan->new(
request => $req,
fields => [ TextField(name => 'name', label => 'あなたの名前: ') ],
);
my $res = $req->new_response(200);
# テンプレート内ではなく、ここでmark_rawしてください
my $form = mark_raw($shakan->render());
$res->body( $tx->render_string(<<'T', { form => $form }) );
<!doctype html>
<html>
<head><title>フォームの構築</title></head>
<body>
<form>
<p>
フォーム:<br />
<: $form :>
</p>
</body>
</html>
T
return $res->finalize();
}
return \&app;
出力はこうなります:
<!doctype html>
<html>
<head><title>フォームの構築</title></head>
<body>
<form>
<p>
フォーム:<br />
<label for="id_name">あなたの名前:</label>
<input id="id_name" name="name" type="text" value="<Xslate>" />
</p>
</body>
</html>
詳細については、HTML::Shakanもご覧ください。
WRAPPER
ディレクティブのスーパーセットである、テンプレートの重ね掛け(template cascading)を使ってください。
wrapper.tx
:
<div class="wrapper">
block content -> { }
</div>
content.tx
: cascade wrapper
: override content -> {
Hello, world!
: }
出力はこうなります:
<div class="wrapper">
Hello, world!
</div>
Xslateは、ブロックモディファイヤーによってテンプレートを拡張することができる、 テンプレートの重ね掛け(template cascading) をサポートしています。 これは伝統的なテンプレートのインクルード(包摂)のようなものですが、より強力なものとなっています。
この機構はテンプレートの継承(template inheritance)とも呼ばれます。
Text::Xslate.ja/"Template cascading"もご覧ください。
Data::Section::Simple
と、 $file_name => $content
のマッピングを含むハッシュリファレンスを受け容れる、 new()
の path
オプションを使ってください。
use Text::Xslate;
use Data::Section::Simple;
my $vpath = Data::Section::Simple->new()->get_data_section();
my $tx = Text::Xslate->new(
path => [$vpath],
);
print $tx->render('child.tx');
__DATA__
@@ base.tx
<html>
<body><: block body -> { :>default body<: } :></body>
</html>
@@ child.tx
: cascade base;
: override body -> {
child body
: } # endblock body
この機能はText::MicroTemplate::DataSectionから着想したものであり、Mojoliciousから引用したものです。
Data::Section::Simple、Text::MicroTemplate::DataSection、Mojoliciousもご覧ください。
XslateがエスケープするのはHTMLのメタ文字だけなので、 <script> ... </script>
セクションでデータを入力する場合、JavaScriptのメタ文字はあなた自身でエスケープしなければなりません。
JSON
モジュールは、"</script>"
のようないくつかのメタ文字をエスケープしないため、ふさわしくありません。
XSSを避けてJavaScriptのエスケープを安全に行うための、実績あるユーティリティーを使うとよいでしょう。 JavaScript::Value::Escapeが、この目的のための一助になります。
my $tx = Text::Xslate->new(
module => ['JavaScript::Value::Escape' => [qw(js)]],
);
my %params = (
user_input => '</script><script>alert("XSS")</script>',
);
print $tx->render_string(<<'T', \%params);
<script>
document.write('<: $user_input | html | js :>');
var user_input = '<: $user_input | js :>';
</script>
T
さらに複雑な場合については、セキュリティーの専門家に助言を求めた方がよいでしょう。
構造化されたテキストを安全な方法で解析するための、銀の弾丸はありません。 それをしたい場合には、セキュリティーの専門家に助言を求めた方がよいでしょう。
いくつかのCPANモジュールがその助けになります。【訳注:helpのlが脱字】一例として、String::Filterをご覧ください。
_()
を含むどんな関数でも 登録することができます。それには特別な技巧は必要ありません。
例えば:
use I18N::Handle;
# I18N::Handleは、グローバル名前空間に対して、ロケールの関数である"_"をインストールします。
# ( *_ シンボルがグローバルであることを忘れないでください)
I18N::Handle->new( ... )->speak('zh_tw');
my $tx = Text::Xslate->new(
function => {
_ => \&_,
},
);
そして、テンプレートでは以下のように記します:
<: _('Hello %1', $john ) :>
I18N::Handle、App::I18Nもご覧ください。
Preforkモデルのアプリケーションでテンプレートを読み込むことは良いアイデアです。 与えられたパスにあるすべてのテンプレートを読み込む例を示します:
use File::Find;
my $path = ...;
my $tx = Text::Xslate->new(
path => [$path],
cache_dir => $path,
);
# 事前読み込みファイル
find sub {
if(/\.tx$/) {
my $file = $File::Find::name;
$file =~ s/\Q$path\E .//xsm; # パス名の是正
$tx->load_file($file);
}
}, $path;
# forkして描画する……
パースする前にコンテンツに対して処理を行うために、Text::Xslateの slurp_template()
をオーバーライドすることができます。
{
package MyTemplate;
use parent qw(Text::Xslate);
sub slurp_template {
my($self, $input_layer, $fullpath) = @_;
my $content = $self->SUPER::slurp_template(
$input_layer,
$fullpath,
);
### ここに $content に対する処理を書きます ###
return $content;
}
}
第一引数の $self はText::Xslateのインスタンスで、第二引数は input_layer
オプションの引数(デフォルトでは :utf8
)で、第三引数はロードするテンプレートファイルのフルパスです。
slurp_template
は テキスト文字列 か バイト文字列
のいずれかを返すでしょう。
このフックは、テンプレートの内容に対して事前処理を行うために提供されていることに気を付けてください。つまりメモリー上にテンプレートを読み込むためには、 SUPER::slurp_template
呼び出す必要があります。