#17 Errata Submission Form

I'm sure that this happens to every author. You write a book, submit the final manuscript to your publisher, and then wait. Finally, after a long time, you get a package in the mail containing your author's copies.

You pull out a copy of your brand-new book and just can't wait to show it to someone. Your wife, your friend, an innocent bystander—it doesn't matter. You just want someone to see it. So you hand them the book, they open it to a random page, and then they say, "I found a mistake...."

One of the worst moments in my life occurred just after I wrote the book Perl for C Programmers. I handed my first book to my wife, who opened it up and said testily, "Who's Karen?"

She was looking at the dedication, which began:

I dedicate this book to Karen, my wonderful wife, who has endured eight months of watching television over the sound of my typing...

My wife's name is not Karen; it's Chi. I had a lot of explaining to do. Turns out the publisher put someone else's dedication in my book.

After a book is published, people will find mistakes in it and send in corrections. This script provides a way for them to do it using the Web.

The Code

  1 #!/usr/bin/perl -T
  2 use strict;
  3 use warnings;
  4
  5 use CGI;
  6 use CGI::Carp qw(fatalsToBrowser);
  7 use HTML::Entities;
  8
  9 my $collector = "[email protected]";
 10
 11 # Message to the user (will get overridden)
 12 my $msg = "Internal error";
 13
 14 my $query = new CGI;    # The cgi query
 15
 16 # The name of the user
 17 my $user = $query->param("user");
 18
 19 # The book information from the form
 20 my $book = $query->param("book");
 21
 22 my $where = $query->param("where");
 23 my $what = $query->param("what");
 24 if (defined($query->param("SUBMIT"))) {
 25     if (not defined($user)) {
 26         die("Required parameter $user missing");
 27     }
 28     if (not defined($book)) {
 29         die("Required parameter $book missing");
 30     }
 31     if (not defined($where)) {
 32         die("Required parameter $where missing");
 33     }
 34     if (not defined($what)) {
 35         die("Required parameter $what missing");
 36     }
 37 }
 38 if (not defined($user)) {
 39     $user = "";
 40 }
 41 if (not defined($book)) {
 42     $book = "";
 43 }
 44 if (not defined($where)) {
 45     $where = "";
 46 }
 47 if (not defined($what)) {
 48     $what = "";
 49 }
 50
 51 $ENV{PATH} = "/bin:/usr/bin";
 52 delete ($ENV{qw(IFS CDPATH BASH_ENV ENV)});
 53
 54 if (($where ne "") or ($what ne ""))
 55 {
 56     $book =~ /([a-z]*)/;
 57     $book = $1;
 58     if (not $book) {
 59         $book = "Strange";
 60     }
 61
 62     open OUT_FILE,
 63       "|mail -s 'Errata for $book' $collector" or
 64         die("Could not start the mail program");
 65
 66     print OUT_FILE "Book: $book
";
 67     print OUT_FILE "User: $user
";
 68     print OUT_FILE "Location: $where
";
 69     print OUT_FILE "Problem:
";
 70     print OUT_FILE "$what
";
 71     close OUT_FILE;
 72
 73     $msg = <<EOF;
 74 <P>
 75 Thank you for your submission.   If you have another
 76 error, fill in the form below.
 77 EOF
 78 }
 79
 80
 81 # Encode the values we are going to print
 82 $user = HTML::Entities::encode($user);
 83 $book = HTML::Entities::encode($book);
 84
 85 print <<EOF;
 86 Content-type: text/html
 87
 88 <HTML>
 89 <HEAD>
 90     <TITLE>Submit an Errata</title>
 91 </HEAD>
 92
 93 <BODY BGCOLOR="#FFFFFF">
 94     $msg
 95     <FORM METHOD="post" ACTION="sub_errata.pl" NAME="errata">
 96         Book:
 97         <SELECT NAME="book">
 98             <OPTION VALUE="vim">
 99                 Vim (Vi Improved)
100             </OPTION>
101             <OPTION VALUE="not">
102                 How not to Program in C++
103             </OPTION>
104             <OPTION VALUE="perlc">
105                 Perl for C Programmer
106             </OPTION>
107             <OPTION VALUE="wcp" SELECTED>
108                 Wicked Cool Perl Scripts
109             </OPTION>
110         </SELECT>
111
112         <P>Your E-Mail address:
113             <INPUT TYPE="text" NAME="user" VALUE=$user>
114         (optional).</P>
115
116         <P>Location of the error:
117             <INPUT TYPE="text" NAME="where">
118         </P>
119
120         <P>Description of the problem:<BR>
121             <TEXTAREA NAME="what" COLS="75" ROWS="10">
122             </TEXTAREA>
123         </P>
124         <P>
125             <INPUT TYPE="submit"
126              NAME="Submit" VALUE="Submit">
127         </P>
128     </FORM>
129 </BODY>
130 </HTML>
131 EOF

Running the Script

As with any CGI program, you run the script by pointing a web browser at it.

The Results

When the script runs for the first time, the user gets a blank form to fill in.


After the mistake is submitted, a confirmation message appears and the user is invited to submit another.


The author will receive an email for each mistake submitted.

Date: Tue, 26 Oct 2004 23:20:42 -0700 (PDT)
From: system user for apache-conf <[email protected]>
To: [email protected]
Subject: Errata for wcp

Book: wcp
User: [email protected]
Location: Errata script
Problem:

The script does not let you pick which edition
of the book has the problem.

How It Works

The script is not that much different than the guest book script, except that it sends email when an input is made.

Now, sending email is normally a fairly simple operation. All you do is open a pipe to the mailer and send the data to it. That simple statement glosses over a host of security concerns.

Problem #1 is the location of the mail program. It is possible for a malicious user to screw up the environment, particularly the PATH environment variable, in an effort to trick the script into running their own program.

But how can a user convince the Apache web server to change the environment? Who said the CGI script was run from Apache? A bad guy with access to an account on your system could run the script manually after playing with the environment.

Fortunately, we are running with the taint check turned on (the -T in the top line), and any attempt to run a command without making the script secure will result in an error such as this:

Insecure $ENV{PATH} while running with -T switch at
script.pl line 1.

Before Perl will let you run a command, you must reset all environment variables that could affect the running of the program:[*]

[*] You can define and use your own environment variables without having to worry about Perl's security logic, such as DEBUG or ENABLE_LOGGING. Only the ones that may affect security must be changed. For more information, see the Perl document: perlsec.

 51 $ENV{PATH} = "/bin:/usr/bin";
 52 delete ($ENV{qw(IFS CDPATH BASH_ENV ENV)});

Now you come to the line that sends the mail. Here's what you would like to write:

open OUT_FILE, "|mail -s 'Errata for $book' $collector"

The problem is that $book is a parameter from the web page. A clever user can manipulate that variable and change it to anything they want. What sort of thing would a hacker put in this variable? How about changing $book to this:

' ; rm -rf /; '

This looks funny until you plug it in the mail statement:

mail -s 'Errata for ' ; rm -rf /; '' [email protected]

So now the shell executes a malformed mail command followed by a perfectly good and nasty hacking command with some other junk tacked onto the end.[**]

[**] There are some problems with this example, which would cause it to fail. But don't try this on your system unless you have lots of time on your hands and good backups. And don't try this on someone else's system unless you have a good lawyer and are willing to spend three to five years away from your computer.

Taint mode is smart enough to detect that $book came from the user and will not let it be used in a command until it is untainted. If you attempt to use user input in a command, Perl will abort your program with an error like this:

Insecure dependency in system while running with -T switch at script.pl line 3.

For the errata script, the only legal $book parameters contain just lowercase letters. So for security, compare the variable against a regular expression to make sure that the input is legal. Anything illegal will get discarded. After this check $book will be untainted:

 56     $book =~ /([a-z]*)/;
 57     $book = $1;

Just because strange things can happen in CGI programs, we check to make sure that $book is set. If it's not, we give it a default value so as not to confuse the rest of the system.

 58     if (not $book) {
 59         $book = "Strange";
 60     }

Checks like these are extremely important because Perl assumes that if you use a regular expression to extract data from a user parameter, you know what you are doing and the result is secure.

Hacking the Script

This program is a good example of a simple two-stage CGI data-collection script. In the first stage, the user fills out the form, and in the second, the form is validated and the data recorded.

Although simple, this script can easily serve as a template for you to produce your own simple (and perhaps not so simple) data-collection scripts.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset