ホーム > perl > Template::Stash::EscapeHTMLByCase(TTでXSS対策)

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

  • 投稿者: chiba
  • 2007/6/15 金曜日 2:41:55
  • perl

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じゃない・・・)とか。

はてなブックマーク - Template::Stash::EscapeHTMLByCase(TTでXSS対策)

コメント:0

コメントフォーム
入力した情報を記憶する

トラックバック:1

この記事のトラックバック URL
https://blog.everqueue.com/chiba/2007/06/15/12/trackback/
トラックバックの送信元リスト
Template::Stash::EscapeHTMLByCase(TTでXSS対策) - へぼい日記 より
trackback - へぼへぼCTO日記 より 2007/6/18 月曜日

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

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

ホーム > perl > Template::Stash::EscapeHTMLByCase(TTでXSS対策)

検索
フィード
メタ情報

ページの上部に戻る