http://www.perlmonks.org?node_id=224782

Perl have nice feature called taint mode which helps developers to write more secure code by forcing them to carefully verify all input. However it doesn't help to prevent certain type of security vulnerabilities like XSS because this type of security vulnerabilities happen when developer doesn't verify and escape output. On first look it seems that taint mode is useless to force checking of output. But I was still thinking if it is really useless in this case. And I've got this idea: how about implementing an additional layer between web applications and its clients which would assure that tainted data cannot pass into web application's output. As result of this meditation I've come up with this merely proof-of-concept implementation which works with Template::Toolkit based applications.

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; }

This is almost drop-in replacement module for Template module which will complain if it notices any tainted data in output. Another missing piece is convenient plugin for Template which would implement HTML/URL escaping in output and untaint escaped strings at same time. Template toolkit provides two plugins useful to do escaping of strings in HTML: Template::Plugin::URL and Template::Plugin::HTML. They only have to be slighly changed to untaint escaped strings.

# 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; }; }

Now example application: nearly hello world :).

#!/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__ <html> <head> <title>Sample program</title> </head> <body> Hello, [% name %]! </body> </html>
#!/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 %] <html> <head> <title>Sample program</title> </head> <body> Hello, [% HTML.escape(name) %]! </body> </html>

P.S. Note that it is merely proof-of-concept just to show the idea. There are probably some missing pieces (for example real implementation should untaint templates which are read by Template Toolkit from filesystem). And I'm sure same idea can be ported to other templating modules.

--
Ilya Martynov, ilya@iponweb.net
CTO IPonWEB (UK) Ltd
Quality Perl Programming and Unix Support UK managed @ offshore prices - http://www.iponweb.net
Personal website - http://martynov.org

Replies are listed 'Best First'.
Re: Using taint mode to prevent XSS holes
by diotalevi (Canon) on Jan 06, 2003 at 23:33 UTC

    Of course there is always Apache::TaintRequest which just ties STDOUT and escapes tainted data. Another generalized approach would be to throw errors if your program attempted to write tainted data. I'd generally prefer to untaint the data myself prior and just use things like that as a constraint system. But anyhow, the base idea is all right there and is really simple.


    Fun Fun Fun in the Fluffy Chair

Re: Using taint mode to prevent XSS holes
by diotalevi (Canon) on Jan 07, 2003 at 16:33 UTC

    On a lark I took a quick trip out to CPAN - here's my suggestion written down as probably-useable code (meaning I didn't test it). It installs a filter on the STDOUT filehandle before script execution so no tainted data is allowed to print. Obviously if you start handling user-input prior to this INIT block then that isn't covered but you shouldn't do that anyway.

    This can be enhanced by using an exception

    package Filter::Handle::Tainted; # Put this code into the package just to have a reasonable # place to live. use Filter::Handle qw(subs); use Taint qw(tainted); use Exception::Class qw(Filter::Handle::Tainted::TaintException); INIT { Filter \ *STDOUT, sub { for (@_) { next unless tainted( $_ ); # Tainted data was about to be printed to # the handle. Throw an exception instead. Filter::Handle::Tainted::TaintException -> throw( error => "Tainted data may not be written to this +io handle." ); } }; }

    Fun Fun Fun in the Fluffy Chair