#!/usr/bin/perl -wT
use strict;
use CGI;
use CGIBook::Error;
my $q = new CGI;
my @missing;
my $month = $q->param( "month" ) or push @missing, "month";
my $year = $q->param( "year" ) or push @missing, "year";
my $key = quotemeta( $q->param( "key" ) ) or push @missing, "key";
if ( @missing ) {
my $fields = join ", ", @missing;
error( $q, "You left the following required fields blank: $fields." );
}
local *FILE;
## This is INSECURE unless you first check the validity of $year and $month
open FILE, "/usr/local/apache/data/$year/$month" or
error( $q, "Invalid month or year" );
print $q->header( "text/html" ),
$q->start_html( "Results" ),
$q->h1( "Results" ),
$q->start_pre;
while (<FILE>) {
print if /$key/;
}
print $q->end_pre,
$q->end_html;
Any user who supplied "../../../../../etc/passwd" as a
month could browse /etc/passwd -- probably
not a feature you want to provide. Assuming that your web form passes
two-digit numbers for months and days, you should add the following
lines:
unless ( $year =~ /^\d\d$/ and $month =~ /^\d\d$/ ) {
error( $q, "Invalid month or year" );
}
You may have noticed that taint mode is enabled and wondered why it
did not catch this security problem. Remember, the function of taint
mode is to not allow you to accidentally use data that comes from
outside your program to change resources outside your program. This
code does not attempt to change any outside resources, so taint mode
sees no reason to stop the script from reading
/etc/passwd. Taint mode will only stop you from
opening a file with an user-supplied filename if you are opening the
file to write to it.
In this example, we were reading from a text file, but this security
issue applies to other forms of data storage too. We could have just
as easily been reading from a DBM file instead. Likewise when you use
a RDBMS, you must specify what database you wish to connect to, and
it is very poor design to allow the user to specify what database to
open and read.