Customize Pages with CGI
CGI can show different pages to different users depending on user preferences.
Exercise:
1) Ask a user to input their name into a textfield and
to choose a color from a popup menu. Then display a page with
a short message (e.g. "Thank you $name for your request") in that color.
Security on CGI Pages
There are several security problems and error sources for CGI scripts
such as the one above.
Here are some security tips:
-
If the script prints user input or html code that is retrieved from the
web apply
$text =~ s/</</g;
$text =~ s/>/>/g;
to the text before printing it.
- For input from radio buttons, menu selections, check boxes, check
all possible values with an if statement. In case of unexpected
values, print an error message and discontinue with the script.
- Don't ask the user for a filename and then write to that file.
- Files that are written to should not be in a directory
that allows server-side includes, active server pages, PHP
pages, or other HTML template systems.
- Don't install CGI code from untrusted sources on your server.
- If you use sendmail to email back to a user, apply
unless ($email =~ /^[\w\.+-]+\@[\w\.+-]+$/) {
die 'Address not in form foo@nowhere.com';
}
to the address and use "sendmail -t -oi"
- Never allow user input into a system() or exec() call
or into an expressions with backticks (`).
- When using open() and calling a program,
always check user input into this statement.
- If your script writes to a file, lock the file so that two
scripts cannot write to it at the same time.
The following tips are listed here for completeness. You'll
understand them when environment variables are discussed
in a couple of weeks.
- If you use Perl -T and system calls (or sendmail), you may need
to include
$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin'; at the beginning
of your script and variables may need to be untainted using a pattern
match.
- Be careful with PATH_INFO. Validate its contents.
- Check the REQUEST_METHOD variable to verify that the data
was send by POST (or GET or ...)
- Check the CONTENT-LENGTH variable to restrict amount of
input text.
More information on security of
CGI scripts,
Perl/CGI and general WWW security
Exercises
2) In the previous script check whether the input is
reasonable and not empty:
- check whether the name and color contain only word characters
or -.
- check that neither name nor color is longer than 100 chars (use the
{100,} multiplier).
If the criteria are not fulfilled, do not
display the results page but instead show an error message.
3) For the $name variable replace HTML characters "<" and ">"
with < and > before printing the name.
Searching a Webpage
The following CGI script expects a URL and a keyword as input from
a form.
#!/usr/local/bin/perl -w
use IO::Socket;
use CGI qw(:standard);
############# beginning of main routine ####################
my $url = param("url");
my $keyword = param("keyword");
start_webpage();
($host, $document) = parse_input($url);
@page_content= read_page($host,$document);
search($keyword, @page_content);
############## end of main routine ###########################
sub start_webpage{
print header();
print "<HTML>
<HEAD>
<TITLE>Search Results</TITLE>
</HEAD>
<BODY>
<H3>Search Results</h3>"
}
##############################################################
sub parse_input {
$current_url = $_[0];
$current_url =~ /(http:\/\/)?([^\/]*)(.*)/;
$host = $2;
$document = $3;
if ($document eq "") {$document = "/";}
return ($host, $document);
}
########################################################################
sub read_page{
my $current_host=$_[0];
my $current_doc=$_[1];
$remote =IO::Socket::INET->new(Proto => "tcp",
PeerAddr => $current_host,
PeerPort => "http(80)",
);
if (!$remote) { die "cannot connect to http daemon on $host"}
$remote->autoflush(1);
print $remote "GET $current_doc HTTP/1.0\n\n";
@output = <$remote> ;
close $remote;
return @output;
}
##################################################################
sub search{
($term,@text) = @_;
print "<p>The results for $term are:";
print @text;
}
Exercises
4) Write a form that lets users input a URL and a keyword and
invokes the CGI script.
5) Change the subroutine "search" so that it actually searches for
the keyword and prints the lines that contain the keyword.
Optional: Less typing
The CGI.pm module provides standard subroutines for printing certain
HTML code fragments. For example,
print h1("Tea");
prints an h1 header.
To use these subroutines, the script must contain the line
"use CGI qw(:standard);".
Abbreviations for writing HTML tags
print header();
print start_html(-title=>"title_goes_here",-BGCOLOR=>'yellow');
print h1("some heading");
print hr();
print p("text");
print start_form();
print a({href=>"url_goes_here.html"},"link text goes here");
print textfield(-name=>'field_name',-default=>'starting value',
-size=>50,-maxlength=>80);
print textarea(-name=>'foo',-default=>'starting value',-rows=>10,
-columns=>50);
print password_field(-name=>'secret',-value=>'starting value',
-size=>50, -maxlength=>80);
print popup_menu(-name=>'menu_name',-values=>['one','two','three'],
-default=>'two');
print scrolling_list(-name=>'list_name',-values=>['one','two','three'],
-default=>['one','three'];-size=>3, -multiple=>'true',
-labels=>\%labels);
print checkbox_group(-name=>'group_name',-values=>['one','two','three'],
-default=>['one','three'],-linebreak=>'true',-labels=>\%labels);
print checkbox(-name=>'checkbox_name', -checked=>'checked',
-value=>'TURNED ON', -label=>'Turn me on');
print radio_group(-name=>'group_name',-values=>['one','two','three'],
-default=>'one', -linebreak=>'true', -labels=>\%labels);
print submit(-name=>'button_name', -value=>'value');
print hidden(-name=>'hidden_name', -default=>['value1','value2']);
print reset();
print end_form();