php: Preventing Form re-submisson - php

I have a php sign up process for uses. When the user gets to the second last page of the sign-up process, it shows a summary of all the signup details and the submit button(Confirm and Pay button basically). Now when the user clicks this button, all those details gets posted to the sql database and that user is created in the DB with a username and password. Now the problem is, if you click that submit button more than once, it resubmits the data and creates duplicate entries in the database, where each duplicate entry has the same details(name, email etc), but different usernames and passwords.
How do I prevent this? I know you can submit and postback to self right after but how do I do that exactly? I tried to implement this code but it does not work: http://www.bjw.co.nz/developer/general/75-how-to-prevent-form-resubmission
Thanks

I would advise a bit of JavaScript in this situation. Add the following to the submit button:
onclick="this.disabled=true;"
This will disable the button once it is clicked so that it can't be clicked again.

You should save a value into the PHP session that identifies if the form is already submitted, using $_SESSION. The PHP Session is locked while each request carries out, so when the first submit locks it, the second will have to wait until the first submit is finished and has set the value. The second request will afterwards read it, and break upon the value being set to already submitted.
Here's an example of how this could be done. It could probably incorporate bugs if one user is doing several registrations at once (why should he?).
On the page that contains the submit button of the form, you should set a marker:
session_start();
$_SESSION["RegistrationSubmitted"] = false;
This says that the registration that a user is currently doing has not been submitted yet.
On the page that is being submitted (POSTed) to, you can check this value:
session_start();
if (isset($_SESSION["RegistrationSubmitted"]) {
if ($_SESSION["RegistrationSubmitted"] == true) {
die("Registration has already been submitted!");
} else {
// Process registration here
$_SESSION["RegistrationSubmitted"] == true;
}
} else {
die("Last registration page hasn't been reached! (A bot?)");
}

Aside from messing with the PHP code or the DOM, why not modify your database table to use unique keys. This way regardless of how many times the user clicks a submit button, the database simply wont care.
This may help you: “INSERT IGNORE” vs “INSERT … ON DUPLICATE KEY UPDATE”

You could create a form key like:
<input type="hidden" name="form-key" value="<?php echo $last_order_datetime; ?>" />
Then, when a new order is submitted, check if the value of $_POST['form-key'] is exactly equal to the most recent order stored.
If it is, process the form.
else, it is a resubmit or some other attempt.

Related

Stopping Duplicate Submission or Double Click Submission

First, I realize that there are other questions asked that are similar to this one, but they do not contain any definitive solutions to my problem.
When a customer submits an order, I need to be able to properly prevent my processing script from running more than once for that particular order.
/payment - the customer fills in all of their details and submits the order
/process_order - collects all of the data and processes the order.
What is happening is that my orders are being processed partially and then processed again fully ... and I can only assume that this is because customers are either double-clicking on the submit button, or are clicking the submit button a second time because the process hasn't completed and the browser hasn't redirected to the 'thank you' page yet...
I could just use jQuery to disable the button upon the first click event, but I'd also like to know what I could do on the server side of things to prevent this from happening.
One possible solution is to create, essentially, a nonce token for your form and store it in the session when showing the form.
$_SESSION['formNonce'] = md5( time() . uniquid() );
When generating the form, add it as a hidden field:
<input type="hidden" name="formNonce" value="<?php echo($_SESSION['formNonce']); ?>">
And then when processing it, the first thing you do is validate the nonce is in the session and then remove it. In this way, a second submission won't pass the validation because the nonce token has been removed.
There would be some tweaking and error checking to all of this, but coupled with the javascript button disabling could be a viable solution.
For example, you can use "anti-forgery tockens" approach. Once user does visit a page, there is a tocken assigned. First attempts to submit the page will remove the tocken from valid list so further attempts would fail.

Use one-time-token field to determine if a message should be displayed

I have a form, which has a "token" field that is unique for each page:
<form ...>
...
<input type="hidden" name="token" value="325324" />
</form>
This unique token gets stored in the database every time is generated.
When the form gets submitted, the controller that handles the processing will check the database to see if the submitted token field value exists in the database and do its stuff it it is.
After the stuff is done, the controller redirects to the same page, but adding a query argument to the url ?updated=1".
The page will display a message notifying the user who submitted the form, that his changes were updated, if that "updated" query argument exists.
So every thing is fine, except that if I refresh the page with the ?updated=1" argument, I get to see the same message, which doesn't really reflect the reality, because the form is not submitted :)
I know this is not that important, because it's not a security issue, but still I'd want to get around it. I found a solution by creating another token, let's call it token2 that I pass to the URL when redirecting as query argument. Then, when the page checks for that updated argument, it will also check if token2 exists in the database. If it does, it will delete it, then display the message. So any further requests that have the same token2 argument in the URL will not trigger the message.
But I don't like the idea of storing two tokens in the database on each page. Can I somehow use the first token to detect if I should show the message or not?
Not sure if it matters, but these one-time-tokens expire after one hour, and all expired tokens get automatically deleted from the db twice a day.
Consider adding another field to this table (DB), named "submitted".
1.User gets to the form (token is generated)
2.Inserting (token , submitted) values ('blabla' , '0')
3.the user submit the form
4.the controller that handles the processing will check the database to see if the
submitted token field value exists in the database and
update the value of submitted to 1.
5.the controller also reload the page with ?update=1 , then you'll have a condition:
if submitted == 1 : update submitted = 0
So , if the user now reload the page (notice that now submitted = 0) you'll show the form and not the message.
Using the DB for these tokens is fine, but I think you may be much better off using the session for this kind of data, especially because it's transient. It could also simplify your code a bit.
As for your specific problem, people seem to forget that you can in fact execute code after you emit the view. Your logic would be something like this:
if ($_SESSION['updated']) {
$view->addUpdatedMessage();
}
$_SESSION['updated'] = false;

Refreshing a web page inserts data again to DB

I've a php web page with a form. After filling the form in web page, I can submit it to the server. But after that, if I refresh the page, it inserts the same record to the database. Is there any solutions to this problem?
Use the POST/Redirect/GET pattern. This will prevent the user from being able to resubmit the same form.
There are a number of ways, for example:
Create a token which you insert into the form (hidden field). if the same token is submitted twice, discard the second submit.
Check in the database if an identical post already exists.
Save the form submit in a session variable.
Redirect the user to a second page after the submit, using the Post/Redirect/Get pattern (preferably in combination with one of the above).
Yep, do two queries. The first checks to make sure the data doesn't already exist in the DB, the second one does the insert if there is no duplicate data. There are a bunch of ways to go about checking to make sure the duplicate data doesn't exist, but this is the basic process you will want to go through.
Well, that's what Refresh means: "do it again."
You can check to see if data that matches the submitted data is already in the database. If so, you can reject the new submission.
Before inserting to the database check whether the same values are duplicates already, and if they are duplicates, don't insert. Checking for multiple columns helps even further. For example, instead of checking just for a "username", you would check for a "username" AND "password".
Obviously the examples above are fake, but you should get the point.
You can make a process.php page and set your form's action to it.
In process.php
//code to insert item to database
header('Location: YOUR_FORM_PAGE_HERE);
Then it will send them back to the original page and there won't be any post data
You could change your query to INSERT IGNORE INTO table_name ...
this will prevent you from inserting twice.

Using hash to check if page with $_POST values was refreshed

When posting a form to the same PHP page, what is the correct method to find if the page was accidentally refreshed instead of submitted again?
Here's what I'm using right now:
$tmp = implode('',$_POST);
$myHash = md5($tmp);
if(isset($_SESSION["myHash"]) && $_SESSION["myHash"] == $myHash)
{
header("Location: index.php"); // page refreshed, send user somewhere else
die();
}
else
{
$_SESSION["myHash"] = $myHash;
}
// continue processing...
Is there anything wrong with this solution?
UPDATE: An example for this scenario would be inserting a row into a registration table. You would only want this operation executing once.
Let's start with the point that you can't distinguish accidental refreshes from purposeful refreshes. So you can only decide to not allow refreshes at all, or in other words require that each form submit must be unique. The best way to do that is to insert a random token into the form.
<?php
$token = /* a randomly generated string */;
$_SESSION['_token'] = $token;
?>
<input type="hidden" name="_token" value="<?php echo $token; ?>" />
After each submit you invalidate the session token. If the token in the session differs from the one submitted with the form, you can discard the POST.
Re comment: You could do this on a per-item basis. For example, here on SO, you may have several question windows open and answer several questions at once. You can make the token on a per-question basis, so the user could have several valid token at any one time, but only one per question.
An example for this scenario would be inserting a row into a registration table. You would only want this operation executing once.
In this case you probably shouldn't be too concerned about the actual POST, but about data consistency as such. You should have some form of unique identification for each user, like his email address. If the address is already registered in the database, you do not register the user again. This is independent of why the user tried to register twice (double submit or actual attempt to register again).
I generally prefer having the POST handler do whatever it needs to do and then redirecting the user to a new page with
header("Location: new-page.php");
so they can refresh without messing anything up
Using tokens in conjunction with the POST/REDIRECT/GET design pattern seems to be the best available solution.
Setting a single-use token would prevent the user from hitting the back button and trying to submit the form again.
Redirecting the user and using a view to display the input allows them to hit refresh if they please.

How do I prevent duplicate entrys to mySQL?

On my site, I have a form that users fill out to become a member. They fill out name, bday, email, etc. Then when they click submit, the data gets into mySQL. But sometimes when a user clicks submit many times or refreshes the page, the data gets inputted to the database more than once. How can I prevent this accidental submission? Is there a code I can use to only let one set of data get into the database?
This is also a problem in my comment section. I allow uses to put comments on people's profiles. But when they abuse the refresh button or submit button, I get like 10 of the same comments. I am trying to prevent users accidentally submitting a comment twice.
Thanks.
Create a UNIQUE constraint:
CREATE UNIQUE INDEX name_of_youw_index ON tablename(columnname);
INSERTs will now fail with double data.
In the first case you probably just want to add a unique index on the email address and use that to create a profile for that user.
In the second case, as I understand it, you are trying to prevent users from accidentally submitting data twice, not to completely prevent data being submitted twice. You probably don't want to prevent people from writing the same comment twice on the same page. If someone writes "Today was like yesterday!", you don't want to prevent them from coming back the next day and writing "Today was like yesterday!" again. This would be unnatural and the check could be expensive as it would require indexing a lot of data. I think you want to prevent someone from submitting the same form twice, regardless of whether the data is the same or not.
So the solution to your second example is to include a hidden field in your form that uniquely identifies it. When they submit the form, mark the value in the hidden field as used. If someone later submits a form with the same value in the hidden field, reject it.
Server-side: Implement throttling. Only allow 1 submission every 10 seconds or so.
Update: When you accept a form submission, record the timestamp you
made the submission in $_SESSION.
When you accept another (or rather,
every) form submission, check if the
value stored in $_SESSION is older
than 10 seconds. If it is, continue.
If it isn't, don't do any more work.
You could do it with just some
database stuff to I guess, but
$_SESSION is much simpler.
Client-side: Disable the submit button via Javascript when the form is submitted.
Make the email field in your database "unique", by adding a unique index to it.
If your database gets another entry with an email which has already been used then mysql throws an error - the error number is 1062.
If you wanted to then you could handle this error different from others.
mysql_errno()
http://www.php.net/manual/en/function.mysql-errno.php
In the case of the database duplicates, put a unique constraint on the field.
For both the database duplicates and the duplicate comments, disable the submit button for 2-3 seconds after it is pressed to prevent multiple submits.

Categories