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) %]!