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

Hi Monks,
I am using CGI script to retrieve the data from html page and store those details in the database. This is working fine.

But the problem is occuring, when we click the refresh (F5) button on the internet explorer. When refresh icon is clicked, the same data is again resubmitted so duplicate data is entered to database.
So i thought of using sessions so as to prevent this. I have gone through the CGI::Session module, but I am unable to implement it.

Can anyone of you pelase give some examples of how to create the sessions and use in my CGI script?

I am using the POST method with the HTML form. but still the data is resubmitted when refresh is clicked
Thanks,
Santhi

Replies are listed 'Best First'.
Re: CGI and Sessions
by dorward (Curate) on Jul 31, 2006 at 14:26 UTC

    Sessions probably aren't the best solution for this.

    In the more general case of protecting against duplicate submissions, I suggest that when you send the form to the client, you include a hidden input containing a unique pseudo-random string (perhaps some combination of the time, the user's ip address and a random number).

    Add this to a new column in your database. When the form is submitted, test to see if that value is already in the database. If it is, then you can either update the record or ignore the new data.

    In the specific case of dealing with refreshes, use the PRG pattern.

    1. Client submits form using POST method (as is proper for changing something in a database)
    2. CGI script processes form request and outputs a 302 HTTP response to redirect the user elsewhere
    3. Client recieves 302 and issues a new request to the server using the GET method.
    4. Server responds with a webpage that displays some information to the client.

    If the user hits refresh, then they'll just request the GET data again and not repost the submission.

Re: CGI and Sessions
by Fletch (Bishop) on Jul 31, 2006 at 14:27 UTC

    Not that it's really a Perl problem, but a common solution is to send a redirect after receiving the POST. Then if the user refreshes they refresh that page, not one sending the POST contents.

Re: CGI and Sessions
by ptum (Priest) on Jul 31, 2006 at 14:21 UTC
Re: CGI and Sessions
by jhourcle (Prior) on Jul 31, 2006 at 14:35 UTC

    I'm going to assume from what you have described that you're passing your parameters through QUERY_STRING or PATH_INFO, and the browser is using an HTTP GET on the CGI script.

    If you're modifying anything based on the result of a request, you should not use GET, you should use an HTML form, with a POST method.

    Modern web browsers will NOT resend the data if the user clicks refresh from a POST. Of course, many will refuse to send anything at all, so you'll want to follow the HTTP recommendation, and send a 201 status and a Location header to point them to the stable URI for the item. (ie, a read-only URI).

    Update: as Ieronim correctly points out -- almost all browsers will send it, but they should prompt first. (I should have said 'will not resend the data without warning', but that's of little condolence, as most users won't understand the implications.) A few browsers also haven't followed the recommendation under History List, and will ask you to resend the POST if you wish to go 'back' in your browser history.

    To deal with both of these issues, I'll repeat myself, and suggest the combination of 201 w/ a Location header to a stable URI for the created/modified record.

      Modern web browsers will NOT resend the data if the user clicks refresh from a POST.
      Firefox will: e.g you can hit "talk" on the right and then press F5 to see this. Yes, it asks if you are sure that you want to resend data, but you cannot suppose that the user won't occasionally press "yes".

           s;;Just-me-not-h-Ni-m-P-Ni-lm-I-ar-O-Ni;;tr?IerONim-?HAcker ?d;print
Re: CGI and Sessions
by Ieronim (Friar) on Jul 31, 2006 at 15:33 UTC
    What kind of DB are you using? If it's SQL-driven, you can define some constrains on the table level protecting from duplicates, like a composite UNIQUE KEY.

    Generally, if you can detect duplicates AFTER adding the data to DB, you can detect them BEFORE you add the data to DB, and display a warning.


         s;;Just-me-not-h-Ni-m-P-Ni-lm-I-ar-O-Ni;;tr?IerONim-?HAcker ?d;print
Re: CGI and Sessions
by aufflick (Deacon) on Aug 01, 2006 at 08:39 UTC
    This is often called "double click protection" or "double submit protection".

    One of the simplest is as advocated in the ecommerce chapter of the venerable panda book by Phillip Greenspun (http://web.archive.org/web/20010119104500/http://www.arsdigita.com/books/panda/ecommerce):


    Reload 5 Times = 5 Orders? The obvious way to implement an ecommerce system is the following:

    1. Serve user a static order form pointing to an "insert-order" page.
    2. When the user hits submit, the insert-order page will run and
      1. generate a unique order ID
      2. insert a row in the database
      3. run the credit card
      4. update the row in the database to say "authorization succeeded"
      5. print a thank-you page back to the user's browser
      6. send email thanking the user for the order
    The problem with this approach is that various aspects of Step 2 may be slow, prompting the user to hit Reload. At which point another unique order ID is generated and another row is inserted into the database and the credit card number is authorized again. If the user hits Reload five times, it looks to the merchant's database just the same as five actual orders. The merchant can only hope that the user will notice the duplicate email messages and alert the merchant's customer service department.

    There are potentially many ways of getting around this problem, but I think the simplest approach instead is to

    1. Serve user a dynamically generated order form that includes a unique order ID, pointing to an "insert-order" page.
    2. When the user hits submit, the insert-order page will run and
      1. insert a row into a database table with a primary key constraint on the order id. If the insert fails (Oracle won't allow two rows with the same primary key), catch it and look to see if there is already an order in the database with the same id. If so, serve the user an order status page instead.
      2. if the insert succeeded, proceed as above

    You might want to use a shared memory store or local disk store if you're concerned about the scalability/performance of using a db, but if you're processing form data you're probably hitting a db already.

Re: CGI and Sessions
by leocharre (Priest) on Jul 31, 2006 at 17:02 UTC

    What I'm curious about here is what kind of stuff are you taking? Comments? Guestbook? Loan application data?

    If the stuff is not sensitive, and it's one time, you could set a cookie that says they submitted.

    The cgi accepting the form data would first check if there is a previous record of this 'user' submitting data through this form.

    Of course they could simply wipe out their cookie and submit again. But this method could be useful if the data is not sensitive, and what you are getting is more an annoyance then a security hole.

    With a cookie and CGI::Session, you could do more interesting things. You can store just one cookie on their machine that identifies them as a client for you- and with a cgi session item, store multiple things like 'submitted_form_a', 'viewed_page_z' etc etc- and also, quite interestingly, you can store complex data structures in a cgi session (even objects?).

    Cpan has very good documentation on how to implement CGI Session and use it to set cookies, retrieve them, etc.