XSS,CGI && Template Toolkit

David Dick david_dick at iprimus.com.au
Mon Nov 18 12:56:59 CST 2002

Ended up just using a custom stash.  Sample code is attached, i needed 
to override the _assign method as well because there was an inheritance 
problem but this seems to vary with your version of TT, so you may not 
need it.  You can also use this to inherit from Template::Stash::XS if 
you want to. (Just change the ISA stuff).  As has been noted on the TT 
mailing list, the Stash isn't the best place to do this sort of stuff, 
because _ideally_ what you want is to apply this sort of filter _after_ 
all the other filters in the template, but it works and it was quick to 
code ;).

Daniel Walmsley wrote:

>Doesn't TT2 have a built-in html filter? [% myval | html %]
>-----Original Message-----
>From: David Dick [mailto:david_dick at iprimus.com.au] 
>Sent: Wednesday, 6 November 2002 6:19 PM
>To: melbourne-pm at pm.org
>Subject: XSS,CGI && Template Toolkit
>Got a bit of a problem with Cross Site Scripting.  The way I've been 
>writing web apps is by using $cgi->param to suck in values from the user 
>and using the Template Toolkit to generate the html.  However, CGI.pm 
>seems to assume that you'll use the CGI.pm routines to output html, so 
>the param method unencodes everything it can, while the CGI.pm output 
>commands encodes them.  Translated..... <INPUT TYPE="TEXT" 
>NAME="Something" VALUE="&lt;SCRIPT&gt;"> will be translated by 
>$cgi->param into <SCRIPT> and the print commands will reencode it as 
>&lt;SCRIPT&gt; to protect against Cross Site Scripting attacks.  The way 
>i've been thinking, CGI.pm does do the correct thing, the place to 
>encode all of that stuff is in the output routine.  I can't find a easy 
>way of doing this in Template Toolkit.  I think I need a automatic 
>FILTER or something.  Anyone else have this problem or come up with an 
>easy solution?
-------------- next part --------------
package MyStash;
use strict;

use Template::Stash();
use HTML::Entities();
use vars qw(@ISA);
@ISA = qw(Template::Stash);

sub get {
	my ($self, $ident, $args) = @_;
	my ($result) = $self->SUPER::get($ident, $args);
	$result = HTML::Entities::encode($result);	
	return ($result);

sub _assign {
    my ($self, $root, $item, $args, $value, $default) = @_;
    my $rootref = ref $root;
    my $result;
    $args ||= [ ];
    $default ||= 0;

#    print(STDERR "_assign(root=$root, item=$item, args=[@$args], \n",
#                         "value=$value, default=$default)\n")
#       if $DEBUG;

    # return undef without an error if either side of the dot is unviable
    # or if an attempt is made to update a private member, starting _ or .
    return undef                                                ## RETURN
        unless $root and defined $item and $item !~ /^[\._]/;

    if ($rootref eq 'HASH' || UNIVERSAL::isa($root, __PACKAGE__)) {
#       if ($item eq 'IMPORT' && UNIVERSAL::isa($value, 'HASH')) {
#           # import hash entries into root hash
#           @$root{ keys %$value } = values %$value;
#           return '';                                          ## RETURN
#       }
        # if the root is a hash we set the named key
        return ($root->{ $item } = $value)                      ## RETURN
            unless $default && $root->{ $item };
    elsif ($rootref eq 'ARRAY' && $item =~ /^-?\d+$/) {
        # or set a list item by index number
        return ($root->[$item] = $value)                        ## RETURN
            unless $default && $root->{ $item };
    elsif (UNIVERSAL::isa($root, 'UNIVERSAL')) {
        # try to call the item as a method of an object

        return $root->$item(@$args, $value)                     ## RETURN
            unless $default && $root->$item();

# 2 issues:
#   - method call should be wrapped in eval { }
#   - fallback on hash methods if object method not found
#         eval { $result = $root->$item(@$args, $value); };
#         if ($@) {
#             die $@ if ref($@) || ($@ !~ /Can't locate object method/);
#             # failed to call object method, so try some fallbacks
#             if (UNIVERSAL::isa($root, 'HASH') && exists $root->{ $item }) {
#                 $result = ($root->{ $item } = $value)
#                     unless $default && $root->{ $item };
#             }
#         }
#         return $result;                                               ## RETURN

    else {
        die "don't know how to assign to [$root].[$item]\n";    ## DIE

    return undef;


More information about the Melbourne-pm mailing list