markjugg has asked for the wisdom of the Perl Monks concerning the following question:

Hello! I frequently develop websites with multi-page forms and need to pass data through the forms efficiently. I developed three routines (below) to do this. My question is: This seems like such a common thing to that either there should also be a solution (that I haven't noticed) such as routines in CGI.pm, or there is a good way to avoid this. Are you all using routines like the ones below? If not, what are you using?
use CGI qw/:standard/; # An efficient way to pass param variables on through a page sub export_form_vars { my @input = @_; my $html = "\n"; foreach my $key (@input) { my @vars = param $key; foreach my $val (@vars) { $val = escapeHTML($val); $html .= "<input type=hidden name=\"$key\" value=\"$val\"> +\n" } } return $html } sub export_form_vars_to_query_string { my @input = @_; my $html; foreach my $var (@input) { $var = escape($var); $html .= '&amp;'."$var=".param($var); } $html =~ s/^&amp;//; return $html } sub export_entire_form { my $html; foreach my $k (param) { my $v = param($k); $html .= '<input type=hidden name="'.escapeHTML($k).'" val +ue="'.escapeHTML($v)."\">\n" } return $html; }

Replies are listed 'Best First'.
Ovid - Security with hidden data in HTML forms
by Ovid (Cardinal) on Oct 14, 2000 at 22:34 UTC
    I use routines similar to what you're doing, though I tend to explicitly write out the hidden fields, since our company doesn't use templates (sigh). One common problem with hidden data is that those hidden values can easily be tampered with. If that's not important to you, it's not a big deal. However, you could have problems if you rely on something like the following:
    <input type=hidden name="price" value="42.95">
    Then, it can be a trivial matter for someone to adjust the price value. Needless to say, if you have other data in those fields that you cannot afford to have altered, this can be a big problem. Try using Digest::MD5 or Digest::SHA1 (SHA1 takes longer, but it's more secure). Here's some sample code:
    #!/usr/bin/perl -w use strict; use Digest::MD5 qw ( md5_base64 ); my $rand = 'yed*73=1/+#@%d'; my $price = '40.95'; my @data = ($rand, $price); my $base64_digest = md5_base64( @data ); print $base64_digest;
    That should print something like "BS1+1ySMDuN+fqp7hnMRYw".

    Take the digest value and embed that in the form. When the values are returned, recompute the digest with the same $rand. If the values don't match, your hidden values have been tampered with. Needless to say, you want $rand to be as secure as possible!

    Cheers,
    Ovid

    Join the Perlmonks Setiathome Group or just go the the link and check out our stats.

      Thanks for the tip Ovid. I use a similar scheme sometimes myself. I learned about this from Writing Apache modules, page 213. In the explanation, they say that you should run the MD5 algorithm twice or "Otherwise, a technically savvy user could take advantage of one of the mathematical properties of the algorithm to append his own data to the end of the fields". They give this example:
      $MAC = MD5->hashhex($secret . MD5->hashex(join '',$secret, @fields) );
Re: functions for passing variables through multiple CGI forms
by cianoz (Friar) on Oct 14, 2000 at 16:48 UTC
    Why not use server side sessions to do that?
    (eg. with Apache::Session)
      I do use server side sessions for a lot of things, but I don't use them all the time because sometimes I simply want to pass data between two forms, and I'm not passing sensitive data, and I'm not 'eval'ing it or using it to create file names, so I have minimal security concerns about the data being hijacked. Adding Apache::Session to the project would add a lot of complexity to the project (it's a lot more code that a bug could potentionally be in. As they say "code deleted (or not used) is code debugged").

      For more complex projects, such as shopping cart systems, I do use server side sessions, but I usually roll my own using a SQL database, rather than adding in the complexity of Apache::Session. It's features of serialization and locking are already provided by the database, retreiving data is done easily through DBI's fetchrow_hashref, and I can insert data easily through CGI::SQL's &insert_from_param function. Furthermore, I'm not tied to having my session table named a particular thing, or having the columns named a particular way.

Re: functions for passing variables through multiple CGI forms
by Perlmage (Acolyte) on Oct 14, 2000 at 22:31 UTC
    I would have to agree with cianoz -- pass state through sessions, and only pass the session key as a hidden variable, or in a cookie. The problem with passing state through a form, besides having to encode/decode it every time, is that you have to untaint/revalidate it every time to keep people from hijacking your variables to their own nefarious ends. If you pass a session key around, they can mess with the key, but the most they can do with a good MD5-hashed key is invalidate their session.
Re: functions for passing variables through multiple CGI forms
by princepawn (Parson) on Oct 16, 2000 at 21:23 UTC
    Under HTML::Embperl this is a snap. On each HTML page that you want hidden fields created for the CGI data passed in your webpage must simply look like this:
    <form> [$ hidden $] </form>
    And Embperl expands it's hidden tag into the CGI query data automatically.

    For comparison, this is not available under HTML::Mason and I cannot comment on the other fairly complete web application frameworks (HTML::EP, Apache::PageKit, and BingoX (which is on sourceforge, not CPAN) with regard to this feature.