prevent double posts

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

Post Reply
GeXus
Forum Regular
Posts: 631
Joined: Sat Mar 11, 2006 8:59 am

prevent double posts

Post 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...
User avatar
Stryks
Forum Regular
Posts: 746
Joined: Wed Jan 14, 2004 5:06 pm

Post 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
GeXus
Forum Regular
Posts: 631
Joined: Sat Mar 11, 2006 8:59 am

Post by GeXus »

Yeah thats a good idea... maybe i'll do that.. thanks!
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post 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.
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
Stryks
Forum Regular
Posts: 746
Joined: Wed Jan 14, 2004 5:06 pm

Post 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
User avatar
Kieran Huggins
DevNet Master
Posts: 3635
Joined: Wed Dec 06, 2006 4:14 pm
Location: Toronto, Canada
Contact:

Post 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.
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post 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.
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
Kieran Huggins
DevNet Master
Posts: 3635
Joined: Wed Dec 06, 2006 4:14 pm
Location: Toronto, Canada
Contact:

Post 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.
jmut
Forum Regular
Posts: 945
Joined: Tue Jul 05, 2005 3:54 am
Location: Sofia, Bulgaria
Contact:

Post 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?
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post 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.
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
GeXus
Forum Regular
Posts: 631
Joined: Sat Mar 11, 2006 8:59 am

Post 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.
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post 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.
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
Post Reply