0

I could see there were several topics created on this matter, but I couldn't really find a solution what could be applied to my case.

My problem is that when a user submits a form on my page, and the browsers stalls some users try to refresh the page while the request is being processed and rows are inserted twice in the database. There are two curl calls in the middle of the script what can take several seconds to complete that's what causes the delay. I beleive that by refreshing the page they open a new connection with the same session details and submit it again, but the abandoned backend script is still working on the process. And the most important (and bad) thing is, the the form posts into itself.

We tried to apply several measures (wiping the SESSION keeping track of a generated session variable etc...) I beleive a hidden field on the form is not an option as the process script could then be attacked with a forged browser.

My suggestion would be to detach the process part from the from the view and migrate it to separate script and call it via Ajax. While the Ajax is being called disable the submit button a prompt the user not to refresh the page while it's being processed. This way even if they refresh the page it won't get submit again and by that time the process can set the SESSION variables to prevent resubmission.

What's your views on this solution?

Please do not post me things like this is bad or that is bad with the process and change this or that as I am not able to make any major change in the process as a whole system builds upon it.

Many Thanks.

////////////////////////////////////// /////////////////////////////////////

Extension:

  • Migrated the process part of the self-posting monster script into an Ajax one.
  • After the ajax is fired I disable the submit button and display a spinner with the text do not refresh the browser
  • when the operation is completed in the Ajax script I set a session variable indicating that the process has been completed
  • If they still refresh the browser, the form data is cleared and they simply won't have enough time to fill it out again, so by the time they come to submit the form again the session variable is already set and on pressing the submit button they are presented with a message and then redirected to the confirmation page.

So far it seems it's working fine had a couple of test runs.

What do you guys think about this solution? Are there any loopholes or security implications? (Error message is coming from the script via Json)

gergiusz
  • 11
  • 1
  • 7
  • add md5 or sha1 to the curl url, the hash is simply the full request URI, store in db last 5 minutes submits in a memory table and disable inserting in case of duplicate, ihad such a situation like this before – Hawili Aug 22 '12 at 09:57
  • What's wrong with simply using a `header()` redirect? The PHP happily continues executing in the background? – Fluffeh Aug 22 '12 at 09:59
  • that's the thing. The curl still processess the request when they refresh and the header() redirect is obviously on the bottom of the page no? – gergiusz Aug 22 '12 at 10:27

3 Answers3

1

ok, another go:

<?php
   session_start();
   $old_session_exists = false;
   while (isset($_SESSION['processing-'.$_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT']])) {
      $old_session_exists = true;
      sleep(5);
   }

   if (!$old_session_exists) {
      // process your cURL and save output into another session variable
      $_SESSION['processing-'.$_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT']] = 1;
      $_SESSION['result-'.$_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT']] = cURL(...);

      // remove processing session
      unset($_SESSION['processing-'.$_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT']]);
   }

   // return the result from session
   $ret = $_SESSION['result-'.$_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT']];
   unset($_SESSION['result-'.$_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT']]);
   return / echo $ret;
?>
Zathrus Writer
  • 4,311
  • 5
  • 27
  • 50
0

gergiusz:

My view on the solution would depend on what exactly are you sending (for example using a classic approach you should not be able to send files through a JS Request). Also, using Ajax would make the application dependant on having JS enabled, would that be a problem?. Finally, even being careful the request could be forged anyway...

Given that, my view is "Please, don't". I think it adds unnecesary complications to the matter.

Though evil and filthy, have you tried sending into a hidden frame?. It's a typical solution that would allow you to show a "Please, wait message" as soon as the form is submitted. As with everything, it can be forged and broken but then again, what isn't?.

The Marlboro Man
  • 971
  • 7
  • 22
  • JS and Jquery is already enabled so it's not an issue. I am only sending textual form data. Thanks, I only put the "please don't" as I have seen in the prevoius questions that people started designing a new system instead of focusing on the problem. True, but still you need to make preventive measures to at least try to make it secure. – gergiusz Aug 22 '12 at 10:51
  • gergiusz: if you're willing to go the JS way I don't really see why you can't do the JS Request. As always, just make sure you disable the possibility of double sending (client and server side if need be) and you'd be allright :). – The Marlboro Man Aug 22 '12 at 11:27
  • @Marlboro: That's what I did eventually: # Server-side: setting the session variable at the key point. # Client-side: disabling the submit button pending the Ajax call. The only loophole is again the refreshing of the page, but the time they'd be able to fill out the empty form again, the Ajax process will have to have been finished and the session var is set. You think that would work? Thanks for the answer... – gergiusz Aug 22 '12 at 17:57
  • gergiusz: I'm not entirely sure this depends on server software or script language, but it could work. Consider this: once the form is submitted into the [evil] frame or the separate script via JS Requests, the separate script itself is on. Set the session variable once the process start (or write a db row with and fetch its primary key to feed it to your session data) and do not start the proccess whenever the flag is on (or even show the form? that's mostly what credit card payment sites do: they won't let you process the same transaction twice). [to be continued...] – The Marlboro Man Aug 23 '12 at 06:18
  • [...continued] How about refreshing the page?. Well, there must always be caveats. As said, I think it would depend on your server software and script language but you can always try and see if lengthy scripts run even after reloading (I am most sure say that they do with my php-apache config, after all, the server is doing the work... Do huge DB queries taking minutes stop when exiting the browser?. They don't, just try and use SHOW PROCESSLIST to see what I mean - whether these queries should exist or not is a different matter). So, for a bit of added learning, just grind and try :). – The Marlboro Man Aug 23 '12 at 06:28
  • @Marlboro: Thanks, your answer was very valuable. I think that's what I am going to do: I am adding one more layer of security by keeping the ajax with session plus a control table in the DB just to make sure. "Set the session variable once the process start": the pro is the script itself is divided into ifs() and naturally only the end it turns out if row insertion is happening or not. – gergiusz Aug 23 '12 at 19:26
  • You're most welcome :). Just in case, keep it monitored for a while. – The Marlboro Man Aug 24 '12 at 06:10
  • Yeah, I will be sending emails to myself with all the details for every transaction in the first few days to see if it's working alright... – gergiusz Aug 24 '12 at 08:25
0

how about using an Asynchronous cURL?

Zathrus Writer
  • 4,311
  • 5
  • 27
  • 50
  • oh sorry, I must've missed that... so you're dependant on the result of that cURL reply then? – Zathrus Writer Aug 22 '12 at 10:57
  • yeah unfortunately. I need the curl results at the end of the script. It's an old system, and old systems are very sensitive for changes :o( – gergiusz Aug 22 '12 at 11:25