|
Securing your Forms from Tampering using MD5
WHAT'S THE PROBLEM?
Form security has been a headache for quite some time, but now the problem
is becoming more well known, and less sophisticated folks are coming up with
ways to alter values contained in hidden input tags, etc... Using the HTTP_REFERER
environment variable to test where your page is being called from prevents
casual formbreakers from succeeding (usually!). Now it seems we need something
even better. So if you want to keep people from running blue light
specials on your site- read on.
WHAT'S THE FIX?
All of BNB's scripts use the HTTP_REFERER validation method, and for things
like non-critical response forms and guestbooks that's fine. If you want to
prevent people from modifying hidden inputs, MD5 is the way to go. MD5 takes
information you feed it, along with a secret password and creates what is
called a hash. Relax, this is a long string of characters that is impossible
to duplicate as long as the hacker-wannabe doesn't know the secret password.
If you came looking for the other hash, sorry...
WHAT DO I NEED?
In order to use the little routine below, your system must have
Digest-MD5 installed on it. This is a perl package that handles
the MD5 encryption. If you do not have it on your server, either install
it or request that your system administrator install it. You can find
the latest copy of Digest-MD5 at
Digest-MD5
or you can download Digest-MD5-2_09_tar.gz
(February 4, 2000) from here. Install using the usual perl Makefile.PL, make, make test, make install
routine.
WHAT DOES THE CODE BELOW DO?
If you have Digest-MD5 installed, you can cut and paste the code below
into your favorite editor and run it on your server using telnet or
ssh. This is NOT a CGI script. If you can't run the script, there is
the actual output below the source code.
This little program simulates a cgi script that creates a form
with three hidden fields, price and sessionid, and a special field
called CONTROL. The program first calls the subroutine make_hash
passing it the two fields we want to protect. It returns CONTROL
which would be sent to the FORM we are creating as a HIDDEN input
type.
It then changes the price from 10.00 to 5.00, like a customer
trying to do a little discount shopping. When the subroutine is
called again sending the same fields, but with the modified data,
the hash changes. We then can make a decision that we have bogus
data and exit our program (or log a potential security breach
attempt).
HOW DO I GET THIS INTO MY SCRIPT?
All you have to do is add the line:
use MD5;
to the top of your script, but below the #!/usr/bin/perl line.
You then cut and paste this code into the bottom of your script:
#------------start-make_hash---------#
sub make_hash{
my @secrets = @_;
my $secret = "";
my $secretpart = "";
foreach $secretpart (@secrets){
$secret .= $secretpart;
}
my $md5 = new MD5;
my $hash = MD5->hexhash(MD5->hexhash ($secret), $magicword);
return $hash;
}
#--------------end-make_hash---------#
HOW DO I MAKE IT WORK?
The idea is to generate your form using a CGI script, and passing
the fields you want to protect to the make_hash subroutine. The
string that is returned, you then place in your form as a HIDDEN
input such as:
<input type=hidden name=control value="$fields{'CONTROL'}>
When you get the results of the form, run the fields you want to
check, and compare the result with the value contained in your
form's CONTROL field. If they match, great! If not, oh well...
The routine can handle lots of fields, but it is critical that
you keep them in the same order when creating and checking the
integrity of the data. Changing the order will change the hash.
#-------------------start-sample-code-------------------------------#
#!/usr/bin/perl
use MD5;
$magicword = "something!hard2guess";
$fields{'price'} = "10.00";
$fields{'sessionid'} = "1001021";
$fields{'CONTROL'}=&make_hash($fields{'price'},$fields{'sessionid'});
print "\n";
print "Our price is 10.00\n";
print "the hash is sent to the form as\n";
print "INPUT NAME=CONTROL VALUE=\"$fields{'CONTROL'}\"\n\n";
print "Let's see what happens if we lower the price to 5.00\n\n";
$fields{'price'} = "5.00";
$CONTROL_NEW=&make_hash($fields{'price'},$fields{'sessionid'});
print "\$CONTROL_NEW = $CONTROL_NEW\n\n";
print "now we test: if ( \$fields{'CONTROL'} ne \$CONTROL_NEW )\n";
if ( $fields{'CONTROL'} ne $CONTROL_NEW ){
print "\nBOGUS!\n\n";
exit;
}
#------------start-make_hash---------#
sub make_hash{
my @secrets = @_;
my $secret = "";
my $secretpart = "";
foreach $secretpart (@secrets){
$secret .= $secretpart;
}
my $md5 = new MD5;
my $hash = MD5->hexhash(MD5->hexhash ($secret), $magicword);
return $hash;
}
#--------------end-make_hash---------#
#---------------------end-sample-code-------------------------------#
#----------------------Sample-Output--------------------------------#
Our price is 10.00
the hash is sent to the form as
INPUT NAME=CONTROL VALUE="c05e1eb70823cdc0e36450d70437e37d"
Let's see what happens if we lower the price to 5.00
$CONTROL_NEW = c1a6b7880d744ec43889e7cb88e49d2a
now we test: if ( $fields{'CONTROL'} ne $CONTROL_NEW )
BOGUS!
|