Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask

Recommendations for client-side state management

by grantm (Parson)
on Nov 22, 2011 at 23:11 UTC ( #939556=perlquestion: print w/replies, xml ) Need Help??

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

I have a web application which uses multi-screen forms - users progress from one screen to the next and when all the necessary data has been collected and validated, some business process is initiated. My current implementation uses fairly standard session state management with a session identifier in a cookie (or URL parameter) used to retrieve a serialised hash from the application database.

Although the application works perfectly well when used as intended, our users encounter problems if they try to complete two separate processes in parallel in different browser windows/tabs. The back button also can screw things up since users complete one sequence with one dataset, start on another with a second dataset then attempt to go back to amend the first (which is now gone from state altogether).

One alternative implementation would be to store the state client-side in a hidden form field rather than server-side in the database. Ideally the implementation would use encryption and a message digest to protect the serialised data against tampering.

I have found the CGI::EncryptForm module. But before I dive in and try it out I wondered if anyone had other modules to recommend or related advice to offer.

  • Comment on Recommendations for client-side state management

Replies are listed 'Best First'.
Re: Recommendations for client-side state management
by fullermd (Priest) on Nov 22, 2011 at 23:56 UTC

    I do similar things with just stuffing data in a hidden field for long stuff like that. I don't see any reason to bother trying to encrypt it; if the user wants to put crap data in, they could already have done that in the first place. Just have to validate it on submission.

    Or, if you really do want to encrypt it, you don't need any special CGI-ish thing for that. Just AES/Serpent/etc the serialized string (whatever format it may be in) and dump that in the form field. Gen up a new key for each session, and just use that for all their forms, should be plenty secure enough.

    An additional option would be to instead of storing in the DB just for the session, create a new little opaque key for each run through a form (a sha256(time()) type construction would be fine; just bitstirred pseudorandomness) and use that and the session for storing it. That way you could have multiple stored saved bits per session, and you just need to stick that key in a hidden field in the form.

Re: Recommendations for client-side state management
by jdrago999 (Pilgrim) on Nov 23, 2011 at 01:54 UTC

    Another option: convert the data to a JSON string base64-encoded JSON string, then sign the string.

    use JSON::XS; use Digest::MD5 'md5_hex'; use MIME::Base64; my $signing_key = '//Sw0rdFiSh//'; my $data = { first_name => 'Will', last_name => 'Smith', age => 39, }; my $json_string = encode_json( $data ); my $signature = md5_hex( $signing_key . $json_string ); my $base64_encoded = encode_base64( $json_string, '' ); # Send the encoded data and the signature in the HTML as hidden fields print <<"HTML"; <input type="hidden" name="_signature" value="@{[ $signature ]}" /> <input type="hidden" name="_data" value="@{[ $base64_encoded ]}" /> HTML

    Later, when processing the stored data on another step:

    my $json_string = decode_base64( $q->param('_data') ); my $new_signature = md5_hex( $signing_key . $json_string ); unless( $new_signature eq $q->param('_signature') ) { die "The data has been tampered with"; }# end unless()
Re: Recommendations for client-side state management
by Anonymous Monk on Nov 23, 2011 at 08:11 UTC

    The problem is choice ;D

    This is session management, I remember reading about it from merlyns stonehenge web techniques columns

    You only use a sessionid (only thing in a cookie) to identify a user (merlyn calls it branding a browser) -- one firefox.exe instance

    You use this sessionid to retrieve a user cache object

    And then, when a user starts a new form, a new uniquely named form, he gets a unique-form-id, and you stuff it in the users-cache

    Each different form has its own form name, gets its own different unique key

    You can pass this form-session-id around in a hidden field

    You can use an abstraction HTML::FormFu::Element::RequestToken - Hidden text field which contains a unique token

    Each time a user starts a form, you give him a new one

    You can ask him to continue an old one

    And then, like a shopping cart, you can offer the user a glimpse into his cart/cache, he see how many forms he started, how far along he is, if he wants to continue editing a form, before final checkout/submission

    Hello shopper, your form-favorite-candy-Q9ef93kdkdlkj3000 started last week is only on page 3 of 12 pages , want to continue?

    The user can start many forms simultaneously, one for each browser window or tab, they're all kept separate

    You can setup cron job, to periodically empty old forms, expire them, and what not ... CHI can help

    I hope I've communicated the concept sufficiently

    For clarification and other ideas you probably might find mod_survey and/or some shopping cart diagram possibly helpful :D

    Yes I English
Re: Recommendations for client-side state management
by TJPride (Pilgrim) on Nov 23, 2011 at 11:32 UTC
    If the user is logged in, you can internally create a sequential ID, link it to the user, and pass it along in a hidden form field. They can't just change the ID and jump into someone else's form, because the ID is linked to the user and the user login is already being verified.

    If the user is not logged in, you can generate some sort of guid (long, random alphanumeric, usually) that they'd never be able to guess, or use a sequential ID like above only with a security code included - the security code being a one-way hash of the ID + some internal key.

    Either way, the simplest way is to just pass along the identifier as a hidden form field if you want them able to do multiple forms simultaneously. This sort of thing is why I only ever use cookies / sessions for user logins, never for form input. ID's + database storage is just so much simpler.

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://939556]
Approved by ww
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (4)
As of 2021-10-17 10:51 GMT
Find Nodes?
    Voting Booth?
    My first memorable Perl project was:

    Results (71 votes). Check out past polls.