Home > perl > Template::Stash::EscapeHTMLByCase(TTでXSS対策)

Template::Stash::EscapeHTMLByCase(TTでXSS対策)

  • 2007-06-15 (金) 2:41
  • perl
  • hatena count

TTでのXSS対策としては下記の2つの方法がまずは考えられます。

1. [% data | html %]のようにFILTERをすべての変数出力部分に使う
2. Template::Stash::EscapeHTMLを使って自動的に全ての変数出力をEscapeする

1は毎回”| html”と書くことになるので、間違いが置きやすいので2を使うことが多いのですが
システム側からescapeしないで出力したいとか、INCLUDEで変数を渡したいとかいうときに問題がでてしまう。
INCLUDEの話はどういうことかというと

たとえば、ヘッダーをheader.ttというファイルにまとめて共有で使っており
渡された変数を用いてタイトルを構成するときなんかがあったとする。
(ちなみにMETAだと変数は渡せない)

content.tt
[% INCLUDE header.tt
title = title_html
%]

header.tt

<html>
<head>
<title>[% title %]</title>
</head>
<body>

まぁこんな構成で下記のように呼び出したとする
#!/usr/bin/perl
use strict;

use Template;
use Template::Stash::EscapeHTML;

my %var = (
title_html => ‘<html>’,
);

my $template = Template->new({
STASH => Template::Stash::EscapeHTML->new(),
});

$template->process(’content.tt’, \%var);
そうするとこれの実行結果は

<html>
<head>
<title>&amp;lt;html&amp;gt;</title>
</head>
<body>

となってしまう。
これは意図した結果ではなく正しくは

<html>
<head>
<title>&lt;html&gt;</title>
</head>
<body>

となってほしいわけですね。
二重にEscapeされてしまう。
これは、STASHのgetは変数の代入の時にも呼ばれている影響です。
代入する度にEscapeされるわけでINCLUDEしなくても単純に

[% title = title_html %]
[% title %]

このようにしていてもやはり同じ問題は起きてしまう。

というわけで、escapeしないことを明示的に指示することをしたい。
こうなってくると最初の1の方法の”| html”でも同じじゃないかと思われるかもしれないが
Order allow, deny
より
Order deny, allow
(Apache用語)
のほうがセキュリティ上は好ましいし、何よりコード量も減る。

で、Template::Stash::EscapeHTMLByCaseってのを作ってみた。
命名はまずいかもしれない。

package Template::Stash::EscapeHTMLByCase;

use strict;
use Template::Config;
use base ($Template::Config::STASH);
our $VERSION = '0.01';

sub get {
    my ($self, @args) = @_;

    if (is_raw(@args)) {
        @args = strip_raw_specifier(@args);
        return $self->SUPER::get(@args);
    }

    my ($var) = $self->SUPER::get(@args);

    unless (ref($var)) {
        return html_filter($var);
    }
    return $var;
}

sub html_filter {
    my $text = shift;

    for ($text) {
        s/&/&amp;/g;
        s/</&lt;/g;
        s/>/&gt;/g;
        s/"/&quot;/g;
    }
    return $text;
}

sub is_raw {
    my @args = @_;

    if (ref $args[0] ne 'ARRAY') {
        return 0;
    }

    if ($args[0]->[0] ne 'RAW') {
        return 0;
    }

    return 1;
}

sub strip_raw_specifier {
    my @args = @_;

    splice @{$args[0]}, 0, 2;

    return @args;
}

1;

使い方はこんな感じにRAWを最初につけてあげる。

content_kai.tt
[% INCLUDE header.tt
title = RAW.title_html
%]

そうすると期待通りに下記の文字列が出力される。

<html>
<head>
<title>&lt;html&gt;</title>
</head>
<body>

と、こんなことをやってみましたが皆さんはこんな状況をどうしてるもんなんでしょ
それ、TemplateToolkitの標準機能で解決できるよ。とかありそうだけど・・・。
なかったらCPANに上げてみたいなぁ(まだCPAN Authorじゃない・・・)とか。

Comments:0

Comment Form
Remember personal info

Trackbacks:1

Trackback URL for this entry
http://www.geminium.com/chiba_blog/2007/06/15/12/trackback/
Listed below are links to weblogs that reference
Template::Stash::EscapeHTMLByCase(TTでXSS対策) from へぼへぼCTO日記
trackback from へぼへぼCTO日記 07-06-18 (月) 2:25

Template::Stash::Filters(TTのSt…

前回のエントリーでTemplate::Stash::EscapeHTMLByCaseというのを作ったわけですが、こうやっていろいろStashで対応しようとすると (more…)

add to hatena hatena.comment (6) add to del.icio.us (0) add to livedoor.clip (5) add to Yahoo!Bookmark (0) Total: 11

Home > perl > Template::Stash::EscapeHTMLByCase(TTでXSS対策)

Search
Feeds
Meta

Return to page top