Снова, остановка кнопки возврата формы повторного представления с nonce

Ребята, я знаю, что эта тема была забита до смерти, но после прочтения здесь многих ответов на вопросы я не приблизился к решению.

Проблема: после отправки формы, даже с Post-Redirect-Get, пользователи могут по-прежнему нажимать кнопку «Назад», чтобы вернуться к исходной форме, которая будет точно такой же, как и при публикации. Затем пользователи могут просто нажать кнопку отправки, чтобы снова отправить те же данные.

Я понимаю, что пытаться отключить кнопку «назад» — плохая практика, поэтому я бы хотел:
1.- чтобы можно было очистить форму так, чтобы введенные пользователем данные больше не появлялись при возврате к опубликованной форме с помощью кнопки «назад».
2.- уметь различать в коде исходную форму и повторную форму.

Я читал, что вы можете достичь # 2 со случайным числом. В Прекратить вставку данных в базу данных дважды Я читаю

Я использую одноразовые номера («число использовалось один раз») для форм, где повторная отправка является проблемой. Создайте случайное число / строку, сохраните их в данных сеанса и добавьте в форму в качестве скрытого поля. При отправке формы убедитесь, что значение POSTed соответствует значению сеанса, а затем немедленно очистите значение сеанса. Повторная подача не удастся. (Джеймс Сокол)

Кажется достаточно ясным, но я не могу заставить это работать. Вот скелетный код, настолько короткий, насколько я могу это сделать. Я пошагово прошел через это. Когда я нажимаю кнопку «Назад», код PHP начинает выполняться с самого начала. $_SERVER['REQUEST_METHOD'] возвращается GET таким образом, как будто форма не была опубликована, и код проваливается и генерирует новое случайное число. Когда форма отправлена ​​(второй раз), $_SESSION['rnd'] равняется $_POST['rnd'] поэтому повторно отправленная форма обрабатывается, как если бы это было в первый раз.

<?php
session_start();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if ($_SESSION['rnd'] == $_POST['rnd']) {
$form_data = $_POST; //process form
$_SESSION['rnd'] = 0;
$msg = 'data posted';
}
else {
$msg = 'cannot post twice';
}
}
else {
$msg = 'ready to post';
$rnd = rand(100,999);
$_SESSION['rnd'] = $rnd;
}
?>
<html>
<head>
<title>app page</title>
</head>
<body>
<h1>app page</h1>
<h3><?= $msg ?></h3>
<form method="post">
Field 1: <input type="text" name="field1"value="<?= (isset($form_data['field1']) ? $form_data['field1'] : '') ?>">
<input type="submit" name="submit" value="Ok">
<input type="hidden" name="rnd" value="<?= (isset($rnd) ? $rnd : $_POST['rnd']) ?>">
</form>

</body>
</html>

Спасибо за понимание. Я понимаю, что приведенный выше код не реализует PRG, но я думаю, что это не имеет значения. Проблема не в обновлении F5, а в обновлении кнопки возврата.

1

Решение

Вам нужно добавить некоторую логику для установки переменной $ _SESSION [‘rnd’], а не просто установить ее в 0 при отправке формы. Вы также можете установить cookie с очень долгим временем ожидания. Я добавил в это печенье.

  session_start();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if ($_SESSION['rnd'] == $_POST['rnd']) {
$form_data = $_POST; //process form
$_SESSION['rnd'] = "POSTED";
setcookie("rnd", "POSTED", time()+3600*24*14); //cookie expires in 2 weeks
$msg = 'data posted';
} else {
$msg = 'cannot post twice';
}
} else {
$msg = 'ready to post';
$rnd = rand(100,999);
if($_SESSION['rnd'] == "POSTED" || $_COOKIE['rnd'] == "POSTED"){
$_SESSION['rnd'] = "POSTED"; //set value of session if you're here because the cookie was detected
$rnd = -100;
} else {
$_SESSION['rnd'] = $rnd;
}
}

Если вы хотите сбросить форму на кнопку назад, вы можете сделать это с помощью этого довольно простого JavaScript.

<script>
var posted = document.getElementById("rnd").value();
if(posted < 0){
document.getElementById("form").reset();
}
</script>

Вам нужно добавить атрибут id «form» в вашу форму, чтобы он работал

  <form id="form" method="post">
1

Другие решения

Это еще не ответ, но я могу отправлять баллы, удалять и изменять их.

RobertSF: я работаю над ответом на этот вопрос, так как я должен понять, что делать в этих обстоятельствах.

Я немного поспал и пью кофе …. 🙂

Я думаю, что использование кнопки «Назад в браузере» — это «красная сельдь».

Если мы сформулируем «требования» программы, то сможем увидеть, что это за проблемы и что мы можем с ними сделать.

1) Как только данные были проверены и приняты из «формы», они становятся «только для чтения». Это не такое уж необычное требование.

2) В данных, принимаемых из формы, нет «очевидного» ключа, поэтому нам нужно сгенерировать «уникальный» ключ для каждой «формы, полной» данных. Также каждая «форма, заполненная» данными, должна рассматриваться как «событие».

3) Пользователь может ввести те же данные, что у нас уже есть, но если они введены под другим «событием», чем они должны быть приняты.

Однако, если мы сможем написать некоторый код, который обеспечивает выполнение этих трех требований, тогда «кнопка возврата браузера и повторная передача» данных, которые мы уже приняли, не должны быть проблемой. Будет сообщено, что он уже находится в базе данных.

Итак, как мы это сделаем?

Мы должны генерировать «ключи событий», которые являются уникальными и также являются ключом к «форме, полной данных».

Мы можем использовать «nonces», чтобы сделать это.

У каждого «nonce» есть «state», например «newRequest», «hasData».

Я напишу полную, но минимальную программу, которая будет соответствовать требованиям. Мы также можем «структурировать» его так, чтобы его можно было легко понять, поэтому я буду использовать структуру «mvc».

Я еще не написал это в форме ‘mvc’. Это просто нуждается в перестройке. Выложу как ответ позже. Ниже приведены некоторые из моих предыдущих мыслей о проблемах.

Каждая форма, которую вы отправляете, ДОЛЖНА иметь уникальный «nonce», в котором ее состояние может быть проверено, когда, возможно, такая же форма возвращается позже! В действительности существует много возможных «активных» форм, каждая из которых имеет «уникальный одноразовый номер» и ассоциированное состояние.

Вот почему я храню их в базе данных.

Если вы хотите использовать $ _SESSION, вы должны использовать массив ‘nonces’ и текущее состояние для каждого из них.

Состояние: установлено в: «hasData», когда пользователь вводит данные в форму, и они принимаются.

1

Я с нетерпением жду ответа Райана Винсента, но тем временем, после долгих потягиваний волос, вот код, который я разработал, чтобы справиться с этой ситуацией. Напомним, что мы говорим о предотвращении повторного представления формы. Однако обстоятельства делают этот случай несколько необычным.

Приложение представляет собой опрос из 150 вопросов, которые профессор предлагает своим студентам в начале и в конце семестра. Опросы проводятся анонимно, и студенты могут сдавать их в любом месте, где есть доступ к Интернету, но многие будут проходить опрос в компьютерной лаборатории школы, где несколько студентов, участвующих в опросе, могут собираться за одним компьютером.

Принять участие в опросе может любой, у кого есть URL для опроса, но мы не беспокоимся о том, что его примут не студенты. Достаточно сложно заставить студентов принять его, но мы не хотим, чтобы студенты, в качестве шутки, могли использовать кнопку «Назад», чтобы вернуться к форме со всеми ее данными, оставленными там, и отправить ее снова, снова и снова и снова , С другой стороны, было бы совершенно правильно, если бы два студента проходили опрос один за другим и представляли одинаковые ответы. Тем не менее, поскольку опросы являются анонимными, мы не можем отличить одного студента от другого, хотя, учитывая длительность опроса, мы не беспокоимся о том, что один и тот же студент заполняет опрос дважды.

В псевдо-коде со вкусом PHP вот что я придумал.

// application.php
start_session()
if form_posted
validate_form
if no_errors
save_form
$_SESSION['form_status'] = 'closed'
header ('Location: confirmation.php')
else
//errors, so redisplay form
$caller = 'process'
include ('form.php')
endif
else //form not posted
if (!isset($_SESSION['form_status']))
//first time displaying form
$_SESSION['form_status'] = 'open'
$caller = 'process'
include ('form.php')
elsif $_SESSION['form_status'] == 'open'
//user refreshed form before submitting it
unset ($_SESSION['form_status'])
header ('Location: application.php')
elsif $_SESSION['form_status'] == 'closed'
//user pressed back button from confirmation_page
header ('Location: confirmation.php')
endif
endif

//confirmation.php
$caller = 'confirmation'
include ('form.php')

//form.php
<form>
fields
fields
fields
//only show submit button when $caller == 'process'
if $caller == 'process'
show_submit_button
elseif $caller == 'confirmation'
link ('click to fill out another form', return.php)
endif
</form>

//return.php
session_start()
unset_session_variables
header('Location: application.php');

Как и во всех сценариях, которые публикуются для себя, сценарий выполняется либо потому, что форма размещена, либо по какой-либо другой причине. Если форма была опубликована, проверьте ее, сохраните данные, отметьте форму как «закрытую» и перенаправьте на страницу подтверждения.

Если форма была не может быть несколько причин, почему мы здесь. Одна из причин заключается в том, что это начало цикла формы, в первый раз. В этом случае отметьте форму «открыть» и покажите форму. Другая причина заключается в том, что пользователь, заполняя форму, обновил ее. В этом случае сбросьте статус формы и дайте сценарию подумать, что это первый запуск. Но если статус формы «закрыт», это может означать только то, что пользователь смотрел на страницу подтверждения и нажимал кнопку «Назад». В этом случае мы просто возвращаем пользователя на страницу подтверждения.

На странице подтверждения есть ссылка для открытия новой формы. Эта ссылка сбрасывает значения в сеансе, а затем перенаправляет в основной сценарий приложения, который затем действует так, как если бы он был в первый раз. Если пользователь отправляет форму и не предпринимает никаких действий на странице подтверждения, а вместо этого отправляется в Yahoo! или YouTube, следующий пользователь, который попытается принять участие в опросе, получит страницу подтверждения с данными, введенными ранее пользователем. Это не проблема, так как этот следующий пользователь может использовать ссылку на странице подтверждения, чтобы создать чистую, свежую форму.

0