#!/usr/bin/perl # # safe-form.pl # # CGI.pm Perl script demonstrating use of an HMAC # to prevent user tampering of "hidden" form field values. # # Written by Advosys Consulting Inc., Ottawa # # Requires: Perl 5 with CGI.pm and Digest::HMAC modules # # Include perl modules: use Digest::HMAC_SHA1; use CGI qw/:standard/; # Pick a secret password used to sign form variables: # Keys should be as follows: # - Picked at random and changed periodically # - Be at least 20 characters (for SHA1) $secretkey = 'soOperSeKretcOderINg'; # Print the MIME header before doing anything else: print "Content-type: text/html\n\n"; print '<html><body>'; print '<h1>Safe input form demo</h1>'; # Assign some example values we don't want changed: $userid = 'ktrout'; $credit_ok = 1; $form_expires = '20051001:12:45:20'; # Display HTML form or check submission: unless ( param('chaddr') ) { # Get an HMAC for our 'hidden' field data: $signature = sigHMAC( 'create',$secretkey,'$userid','$credit_ok','$form_expires'); print_form(); } else { # Validate signature if ( sigHMAC('check',$secretkey,'userid','credit_ok','form_expires') eq param('signature') ) { print "\nThank you ", param('userid'), "<br>"; print "\nYour address information has been updated."; } else { print "\nERROR: 'Hidden' fields were tampered with!"; } } print "</body></html>"; ### SUBROUTINES: # sub print_form { # Prints example HTML form with signature in a hidden field: print<<END_TEXT; <p><form action="http://advosys.ca/cgi/safe-form.pl" method="post"> <table> <tr> <td><b>Address line 1:</b></td><td><input type="text" name="address1"></td> <tr> <td><b>Address line 2:</b></td><td><input type="text" name="address2"></td> <tr> <td><b>City:</b></td><td><input type="text" name="city"></td> <tr> <td><b>Prov:</b></td><td><input type="text" name="prov"></td> <tr> <td><b>Postal:</b></td><td><input type="text" name="postal"></td> <tr><td colspan="2" align="center"> <input type="hidden" name="userid" value="$userid"> <input type="hidden" name="credit_ok" value="$credit_ok"> <input type="hidden" name="form_expires" value="$form_expires"> <input type="hidden" name="signature" value="$signature"> <input type="submit" name="chaddr" value="Change address"> </td></tr> </table> </form> END_TEXT } sub sigHMAC { # Generates a SHA1 HMAC from a secret key and either a list of # variables for a list of CGI.pm field names. # # # Usage: # sigHMAC( 'create', $secretkey, '$perlvar1' [, '$perlvar2' ] ... ); # or # sigHMAC( 'check', $secretkey, 'ParamName1', [, 'ParamName2' ] ... ); # my $mode = shift; my $key = shift; my @names = @_; my $values = ''; my $fieldname; # Create a new HMAC object with our key: $hmac = $hmac = Digest::HMAC_SHA1->new($key); # Join each variable name with it's value: foreach $fieldname (@names) { if ($mode eq 'create') { $values .= $fieldname . eval $fieldname; } else { $values .= '$' . $fieldname . param($fieldname); } } # Add our data: $hmac->add( $values ); # Return a Base64 encoded HMAC for the data: return $hmac->b64digest; }