Reputation: 40289
I am trying to prevent the user from double submitting the forum by adding token hidden field.
So here is what I have done so far (before the forum loads I have this code to create a token with the current time as a value.
$token = time();
setcookie('formToken', $token, time() + 3600);
in my forum I have a hidden input like this
<form method="post" action="'.$PHP_SELF.'?action=update">
<input type="hidden" name="token" value="'.$token.'" />
<input type="submit" value="go" />
</form>
now on the top of my page where $action == "update" I have this code
if(isset($_POST) && ($_POST['token'] != $_COOKIE['formToken'])){
$error_list .= '<li>You can not submit this forum twise.</li>';
}
if i hit F5 to refresh the page it submit the form again without displaying my error.
Upvotes: 6
Views: 19564
Reputation: 11403
I suggest you to use use the PRG pattern (Post/Redirect/Get), which is also implemented by forums like phpbb
.
Post/Redirect/Get (PRG) is a web development design pattern that prevents some duplicate form submissions, creating a more intuitive interface for user agents (users). PRG implements bookmarks and the refresh button in a predictable way that does not create duplicate form submissions.
Upvotes: 11
Reputation: 11
I had the same problem, here is a simple fix:
if(!empty($_SESSION['form_token']) && time() - $_SESSION['form_token'] < 3){
$data['message'] = 'try again later';
return;
}
$_SESSION['form_token'] = time();
In my case the PRG pattern didn't have any effect since form submitted multiple times at the same time and the code had not been executed and there is no data saved to compare it against.
Upvotes: 0
Reputation: 4442
For the same issue I made a code to use it for my own stuff. It has the PRG pattern and flexible to use it on same page or with extern PHP file for redirection - Easy to use and safe, maybe this might help you.
class unPOSTer {
private
$post = "KEEP_POST";
public function __construct(string $name = null) {
if (version_compare(PHP_VERSION, "5.4.0") >= 0) {
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
} else {
if (!$_SESSION) {
session_start();
}
}
$this->post = $name;
}
public function unPost() {
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start();
} elseif (strcasecmp($_SERVER["REQUEST_METHOD"],"POST") === 0) {
$_SESSION[$this->post] = $_POST;
header("Location: " . $_SERVER["PHP_SELF"] . "?" . $_SERVER["QUERY_STRING"]);
exit;
} elseif (isset($_SESSION[$this->post])) {
$_POST = $_SESSION[$this->post];
}
}
public function retrieve($data) {
if (isset($_SESSION[$this->post])) {
$posts = @$_SESSION[$this->post][$data];
if (isset($posts)) {
return $posts;
} else {
return null;
}
}
}
public function reset() {
if (isset($_SESSION[$this->post])) {
unset($_SESSION[$this->post]);
}
}
}
Then use it like this:
<?php
require_once "unPOSTer.class.php";
$unpost = new unPOSTer();
$unpost->unPost();
?>
<form action='' method=POST>
<input type=text name=fname value="<?php echo $unpost->retrieve("fname"); ?>" placeholder="First Name">
<input type=text name=lname value="<?php echo $unpost->retrieve("lname"); ?>" placeholder="Last Name">
<input type=submit name=send value=Send>
</form>
<?php echo $unpost->reset(); ?>
Not much to configure, do it on every page you send form data if you like. The retrieve()
method spits out the data you have sent, in case if you might go back and fix something. Feel free to fork/pull it at my GitHub page I added 2 demos there.
Upvotes: 0
Reputation: 1014
Why not just set a session when the form is successfully submitted?
so $_SESSION['submitted'] = 1
;
Then you can check for it.
Or Do
if(isset($_POST['submit']) && ($_POST['token'] != $_COOKIE['formToken'])){
$error_list .= '<li>You can not submit this forum twice.</li>';
}
Upvotes: 3
Reputation: 1
I use this way of preventing double form submissions, it has worked on all occasions so far. Let me know if you need additional questions as this tutorial assumes you have intermediate knowledge on database and PHP.
STEP 1 : add a field on your database like this: replace YOUR-TABLE with the name of your database table.
ALTER TABLE `YOUR-TABLE` ADD `token` VARCHAR(35) NULL DEFAULT NULL AFTER `creationtoken`, ADD UNIQUE (`token`) ;
STEP 2 on your form page you add this to the very first line: it will create a unique toke that will be inserted into your database table along with you query, so that it can be checked for later to make sure no other like it is submitted into your database, meaning no double form submissions.
<?php
session_start();
date_default_timezone_set('America/Chicago');
$_SESSION['token'] = md5(session_id() . time());
?>
then just before your submit button add this:
// add this before the submit button
// this will post the unique token to the processing page.
<div style="width:100%; color:#C00; font-weight:normal;">Session Token: <?php echo strtolower($_SESSION['token']) ?></div>
<input type="hidden" name="token" id="token" value="<?php echo $_SESSION['token']?>" />
// add this before the submit button
<input type="submit" id="submit" name="submit" class="button" value="Submit" />
STEP 3: now on your process.php page
//this is where all of your form processing takes place.
// this is where you call the database
// if you need the database file let me know...
include("../common/databaseclass.php");
$db= new database();
//here the token is posted then the database table is checked and
//if the form has already been added it will return a 1 and will
//cause the query to die and echo the error message.
$token = $_POST['token'];
$query = "SELECT token FROM YOURTABLE WHERE token = '$token' LIMIT 1";
$result = $db->query($query);
$num = mysql_num_rows($result);
if ($num>0) {die('your form has already been submitted, thank you');}
else {
$host = "localhost";
$user = "user";
$pass = "password";
$db_name = "database";
mysql_connect($host,$user,$pass);
@mysql_select_db($db_name) or die( "Unable to select database");
// table query
$sql1="INSERT INTO YOURTABLE (
`token`,
`user`,
`email`,
`password`,
`newaccount`,
`zipcode`,
`city`,
`state`,
`country`,
`telephone`,
`creationip`,
`createdaccount`
)
VALUES (
'$token',
'$username',
'$email',
'$password',
'$newaccount',
'$zipcode',
'$city',
'$state',
'$country',
'$phone',
'$ipadress',
'$createdaccount'
)";
$db->query($sql1);
header("location:" http://home.php ");
}
Upvotes: 0
Reputation: 51
gd1 answer will not prevent double click submission or accidental double submit by various jQuery bindings on a complex javascript form code.
Double click may be even faster then disabling submit button, or hiding it with javascript, so this would not be a full answer either.
The session token will not work either because session is not yet written and thus available or updated for the second process which may be just milliseconds away sharing the same session ID. The session is stored only upon completion of the fist process.
Cookie technique could be an answer as far as both processes are able to communicate over cookie in a blocking way, which may result to the same problems as the session sharing above.
The best solution would be to use server's shared memory access to check if the other process had already processed the data (order, payment, etc..) with the pregenerated data hash, or use database table blocking select and insert to check if the pregenerated hash has been already submitted.
Upvotes: 3
Reputation: 5038
Suggestion 1)
on Successful Submission Delete the cookies (removeTokens)
function removeToken()
{
//set formToken cookie val to "" (or any default xxxx) and the past expiry date for it
setcookie("formToken", "", time()-3600);
//try to unset - this is not needed ,we may try it
unset($_COOKIE['formToken']);
}
ie simply on your page if(isset($_POST)) removeToken();
Suggestion 2)
Perform a redirect as suggested by Tom Wright here Avoiding form resubmit in php when pressing f5
header('Location: formsubmitSucess.php');
Upvotes: 0