Предотвращение отправки двойной формы с использованием токенов
Я пытаюсь запретить пользователю дважды отправлять форум, добавляя скрытое поле токена.
Итак, вот что я сделал до сих пор (до загрузки форума у меня есть этот код для создания токена с текущим временем в качестве значения.
$token = time(); setcookie('formToken', $token, time() + 3600);
на моем форуме у меня есть скрытый ввод, подобный этому
<form method="post" action="'.$PHP_SELF.'?action=update"> <input type="hidden" name="token" value="'.$token.'" /> <input type="submit" value="go" /> </form>
теперь в верхней части моей страницы, где $ action == "update" У меня есть этот код
if(isset($_POST) && ($_POST['token'] != $_COOKIE['formToken'])){ $error_list .= '<li>You can not submit this forum twise.</li>'; }
если я нажму F5, чтобы обновить страницу, он снова отправит форму, не показывая мою ошибку.
6 Solutions collect form web for “Предотвращение отправки двойной формы с использованием токенов”
Я настоятельно рекомендую вам избегать вашей системы: она не проверена глубоко, вы будете тратить время на ее отладку, и она не избежит печально известного «Отправить POSTDATA снова»? диалоговое окно подтверждения браузера, которое запутывает пользователя.
Я предлагаю вам использовать шаблон PRG (Post / Redirect / Get), который также реализуется на форумах, таких как phpbb
.
Post / Redirect / Get (PRG) – это шаблон проектирования веб-разработки, который предотвращает некоторые дубликаты форм, создавая более интуитивно понятный интерфейс для пользовательских агентов (пользователей). PRG реализует закладки и кнопку обновления предсказуемым способом, который не создает дубликаты форм.
Ваша проблема уже решена, решение здесь, просто возьмите ее – переключение вашего собственного похожего-на-решение будет только тратить ваше время. 🙂
Ответ gd1 не будет препятствовать представлению двойного щелчка или случайному двойному отправке различными связями jQuery в сложный код формы javascript.
Двойной щелчок может быть еще быстрее, а затем отключить кнопку отправки или скрывать его с помощью javascript, так что это не будет полным ответом.
Маркер сеанса не будет работать либо потому, что сеанс еще не написан и, таким образом, доступен или обновлен для второго процесса, который может находиться всего в миллисекундах, используя один и тот же идентификатор сеанса. Сессия сохраняется только после завершения первого процесса.
Техника Cookie может быть ответом, поскольку оба процесса способны связывать куки-файлы блокирующим образом, что может привести к тем же проблемам, что и совместное использование сеанса.
Лучшим решением было бы использовать доступ к общей памяти сервера, чтобы проверить, не обработал ли другой процесс данные (заказ, оплата и т. Д.) С помощью хэша предварительно сформированных данных, или использовать блокировку таблицы базы данных, выбрать и вставить, чтобы проверить, является ли прегенерированный хэш уже представлен.
Почему бы просто не установить сеанс, когда форма успешно отправлена?
поэтому $_SESSION['submitted'] = 1
;
Тогда вы можете проверить это.
Или сделай
if(isset($_POST['submit']) && ($_POST['token'] != $_COOKIE['formToken'])){ $error_list .= '<li>You can not submit this forum twice.</li>'; }
Предложение 1)
по успешному представлению Удалите файлы cookie (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']); }
с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']); }
т.е. просто на вашей странице, если (isset ($ _ POST)) removeToken ();
Предложение 2)
Выполните перенаправление, как предложил Том Райт. Избегайте повторной отправки формы в php при нажатии f5
header('Location: formsubmitSucess.php');
Я использую этот способ предотвращения представлений о двойной форме, он работал во всех случаях до сих пор. Дайте мне знать, если вам нужны дополнительные вопросы, поскольку в этом учебном пособии предполагается, что вы имеете промежуточные знания по базе данных и PHP.
ШАГ 1: добавьте в свою базу данных следующее: замените YOUR-TABLE именем вашей таблицы базы данных.
ALTER TABLE `YOUR-TABLE` ADD `token` VARCHAR(35) NULL DEFAULT NULL AFTER `creationtoken`, ADD UNIQUE (`token`) ;
ШАГ 2 на вашей странице формы вы добавите это в первую строку: она создаст уникальный toke, который будет вставлен в вашу таблицу базы данных вместе с запросом, чтобы впоследствии можно было проверить, чтобы никто другой, представленные в вашу базу данных, что означает отсутствие представлений о двойной форме.
<?php session_start(); date_default_timezone_set('America/Chicago'); $_SESSION['token'] = md5(session_id() . time()); ?>
то перед тем, как кнопка отправки добавит следующее:
// 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" />
ШАГ 3: теперь на странице process.php
//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 "); }
в//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 "); }
По той же проблеме я сделал код, чтобы использовать его для своих собственных вещей. Он имеет шаблон PRG и гибкий, чтобы использовать его на той же странице или с внешним файлом PHP для перенаправления. Простота использования и безопасность, возможно, это может вам помочь.
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]); } } }
с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]); } } }
Затем используйте его следующим образом:
<?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(); ?>
Не так много, чтобы настроить, сделайте это на каждой странице, которую вы отправляете данные формы, если хотите. Метод retrieve()
выплескивает данные, которые вы отправили, в случае, если вы можете вернуться и исправить что-то. Не стесняйтесь развивать / тянуть его на моей странице GitHub. Я добавил 2 демо.