package Template::Secure;
use strict;
use warnings;
use base qw(Template);
use Carp;
use Scalar::Util qw(tainted);
# simplified version of Template's process method which only supports
# output to STDOUT
sub process {
my ($self, $template, $vars) = @_;
my $output = '';
my $ret = $self->SUPER::process($template, $vars, \$output);
if(tainted $output) {
croak("Insecure dependency in Template::Secure->process()");
}
print $output;
return $ret;
}
####
# a bit hacky way to redefine subs without modifying sources; this
# code can be put directly into Template::Secure module
{
require Template::Plugin::URL;
require Template::Plugin::HTML;
no warnings 'redefine';
my $url_escape_sub = \&Template::Plugin::URL::escape;
*Template::Plugin::URL::escape = sub {
my $ret = $url_escape_sub->(@_);
$ret =~ /(.*)/; # untaints string
return $1;
};
my $html_escape_sub = \&Template::Plugin::HTML::escape;
*Template::Plugin::HTML::escape = sub {
my $ret = $html_escape_sub->(@_);
$ret =~ /(.*)/; # untaints string
return $1;
};
}
####
#!/usr/bin/perl -T
# First version which has XSS hole and doesn't work thanks to taint
# checks in Template::Secure
use strict;
use warnings;
use CGI;
use Template::Secure;
my $query = CGI->new;
my $name = $query->param('name') || 'World';
my $tt = Template::Secure->new;
print $query->header;
$tt->process(\*DATA, { name => $name }) || die $tt->error(), "\n";
__END__
Sample program
Hello, [% name %]!
####
#!/usr/bin/perl -T
# Second version which is XSS free
use strict;
use warnings;
use CGI;
use Template::Secure;
my $query = CGI->new;
my $name = $query->param('name') || 'World';
my $tt = Template::Secure->new;
print $query->header;
$tt->process(\*DATA, { name => $name }) || die $tt->error(), "\n";
__END__
[% USE HTML %]
Sample program
Hello, [% HTML.escape(name) %]!