Page 1 of 1

prevent double posts

Posted: Tue Oct 30, 2007 10:15 pm
by GeXus
How do you guys prevent double submits on a form? Basically the form posts then header redirects, but it's possible a person could hit that submit button real quick and enter two records... the tables in the db are auto-increment nor are their any unique columns...

Posted: Tue Oct 30, 2007 10:41 pm
by Stryks
I've never used this approach to fix this exact problem, but I'd imagine it would work.

You could generate a single-use key and embed it as a hidden field in your form.

Basically, a random key is generated and inserted into your form. It is also saved into a SESSION.

Then when the form is processed, it checks the submitted key and if it matches, it processes the form. Once processed, the key is removed from the session.

When the second submission arrives, the key is missing and the form refuses to process.

Of course, you'd have to find a non-confusing way to let the user know. Maybe a redirect to the normal after-post page with a notice saying that a multiple or unauthorized post was detected - check to ensure post was successful.

This will also prevent other sites manufacturing forms to submit to your site.

A code snip from somewhere ...

Code: Select all

function new_token() {
	$token = md5(uniqid(rand(), true));
	$_SESSION['FORM_TOKEN'] = $token;
	$_SESSION['FORM_TIME'] = time();
	return $token;
}

function check_token($token) {
	if(isset($_SESSION['FORM_TOKEN']) && isset($_SESSION['FORM_TIME'])) {
		if((time() - $_SESSION['FORM_TIME']) <= 300) {
			if($_SESSION['FORM_TOKEN'] == $token) {
				unset($_SESSION['FORM_TOKEN']);
				unset($_SESSION['FORM_TIME']);
				return 0;					// Success
			} else return 1;				// Failure - token invalid
		} else return 2; 					// Failure - token timeout
	}
	return 1; 								// Failure - token invalid
}
Not my best code, but you can see the idea. The first function generates a time limited single-use code to be inserted into a form, and the second checks the session token against the returned token to ensure that it is the same and that the time limit of 300 seconds has not expired. Unlike me, but clearly I was returning failure codes - normally I wouldn't use 0 or 1 for this as they can evaluate to true or false, but anyhow - so it will return 0 on success, 1 for failed token and 2 for timeout.

Anyhow ... not saying this is the greatest code in the world, but it should point you in the right direction.

Hope this helps

EDIT | Just thought it would be fun to add the other function to create the token. At least it wasn't a totally forgotten email attachment, which is my usual slip-up

Posted: Tue Oct 30, 2007 11:25 pm
by GeXus
Yeah thats a good idea... maybe i'll do that.. thanks!

Posted: Tue Oct 30, 2007 11:42 pm
by s.dot
Well, my approach is not for that reason -- moreso security -- but it will work for that. And adds a layer of security to your web site.

I generate a random token.

Code: Select all

$token = md5(microtime());
I then store it in a hidden input field in the form.

Code: Select all

echo '<input type="hidden" name="token" value="' . $token . '" />';
Then I store the page and their user id in a database table.

Code: Select all

mysql_query("INSERT INTO `tokens` (`token`, `page`, `user`) VALUES ('$token', '$page', '$userId')") or die(mysql_error());
Then when they submit the form, I check that the token exists in the database with the same credentials.

This:

a) Ensures the form submission came from my site.
b) Ensures the user posting the form has sufficient permissions to post the form.
and c) I guess it prevents double posting since once the form is processed, the token is removed from the database. If they tried to submit in the manner you mentioned, it would give them an error.

Posted: Tue Oct 30, 2007 11:57 pm
by Stryks
Is there any real need to save a single use value to the database? Especially when the token system is best applied with a time limit?

Not nitpicking or anything, just wondering why that would be better than temp storage in a session.

Cheers

Posted: Tue Oct 30, 2007 11:58 pm
by Kieran Huggins
I've used that method and I've been satisfied.

If you don't want to use sessions, you can just (try to) enter the form token in your DB as an extra (unique) column. That wouldn't protect against form manufacturing, but would prevent double posts.

Posted: Wed Oct 31, 2007 12:02 am
by s.dot
Stryks wrote:Is there any real need to save a single use value to the database? Especially when the token system is best applied with a time limit?

Not nitpicking or anything, just wondering why that would be better than temp storage in a session.

Cheers
It wouldn't be. It was about two years ago when I designed my token system. Storage is storage, choose a method. :P I suppose session storage would be easier on system resources.

Posted: Wed Oct 31, 2007 12:08 am
by Kieran Huggins
wow - that's what I get for opening up threads in tabs and making tea. Ignore my previous post, it was far more thoroughly covered by scottayy.

Posted: Wed Oct 31, 2007 9:55 am
by jmut
scottayy: interesting

What if there is no userid? I would assume you use this token thingy on all forms, not just user specific. Then I guess you just don't fill in that user column.
But why would that guarantee to be post from your site?
Wouldn't it be possible to open page as usual...see the token...and forge request with valid token and spoofed form?

Posted: Wed Oct 31, 2007 12:36 pm
by s.dot
jmut wrote:scottayy: interesting

What if there is no userid? I would assume you use this token thingy on all forms, not just user specific. Then I guess you just don't fill in that user column.
But why would that guarantee to be post from your site?
Wouldn't it be possible to open page as usual...see the token...and forge request with valid token and spoofed form?
Sure, but it's a one time use token, so automation would be a bit hard. But, all of my forms I use the token on are when the user is logged in. So the combination check of the session for user id and the database for the token makes it pretty hard to fake it.

Posted: Wed Oct 31, 2007 10:16 pm
by GeXus
Is there really a need to even have the hidden field? What if when the form was submitted, it added a token into session... but before doing that I check if the token exists, if it does exist this would mean they have already posted the form otherwise It would add it to session and move on to the next page, then when the form process is complete I just kill the session?


So... User hits submit button, I check if token exists, if it does, ignore the db call and simply hit the header redirect, if it doesn't exist, add it and redirect.

Posted: Thu Nov 01, 2007 1:05 am
by s.dot
A database token allows you to check against something. But, storing the time the token was made in the session may be helpful too.