Как предотвратить множественные вставки при отправке формы в PHP?

Иногда пользователь может дважды нажать Enter, и сообщение будет вставлено дважды.

Есть ли решение, чтобы предотвратить это, кроме как проверить, есть ли уже сообщение с теми же title и content?


person user198729    schedule 25.01.2010    source источник
comment
Можно ли отключить кнопку после ее нажатия?   -  person BenB    schedule 25.01.2010
comment
Шлепайте их каждый раз, когда они это делают. В конце концов они научатся :)   -  person Gordon    schedule 25.01.2010
comment
Я бы рекомендовал использовать ключи формы. Взгляните на это здесь: net.tutsplus. com/tutorials/php/secure-your-forms-with-form-keys   -  person Fábio Antunes    schedule 26.01.2010


Ответы (15)


Есть несколько решений этой проблемы:

  1. Используйте Javascript, чтобы отключить кнопку отправки формы при ее публикации. Недостатком этого является то, что это АБСОЛЮТНО ненадежный способ. Очень легко отправлять формы, фактически не нажимая кнопку, и это также не будет работать для пользователей с отключенным JavaScript. Я определенно не рекомендую этот метод.

    Пример:

    <script language="javascript">
    <!--
        function disableSubmitButton() {
            // you may fill in the blanks :)
        }
    -->
    </script>
    <form action="foo.php" method="post">
        <input type="text" name="bar" />
        <input type="submit" value="Save" onclick="disableSubmitButton();">
    </form>
    
  2. Используйте сеансы PHP для установки переменной сеанса (например, $_SESSION[ 'posttimer']) на текущую отметку времени при публикации. Перед фактической обработкой формы в PHP проверьте, существует ли переменная $_SESSION['posttimer'] и проверьте наличие определенной разницы отметок времени (IE: 2 секунды). Таким образом, вы можете легко отфильтровать двойные отправки.

    Пример:

    // form.html
    <form action="foo.php" method="post">
        <input type="text" name="bar" />
        <input type="submit" value="Save">
    </form>
    
    // foo.php
    if (isset($_POST) && !empty($_POST)) 
    {
        if (isset($_SESSION['posttimer']))
        {
            if ( (time() - $_SESSION['posttimer']) <= 2)
            {
                // less then 2 seconds since last post
            }
            else
            {
                // more than 2 seconds since last post
            }
        }
        $_SESSION['posttimer'] = time();
    }
    
  3. Включите уникальный токен в каждый POST. В этом случае вы также должны установить переменную сеанса для токена, который вы хотите включить, а затем отобразить токен в форме. После отправки формы вы повторно генерируете токен. Если отправленный токен не соответствует токену в вашем сеансе, форма была повторно отправлена ​​и должна быть объявлена ​​недействительной.

    Пример:

    // form.php
    <?php
        // obviously this can be anything you want, as long as it is unique
        $_SESSION['token'] = md5(session_id() . time());
    ?>
    <form action="foo.php" method="post">
        <input type="hidden" name="token" value="<?php echo $_SESSION['token'] ?>" />
        <input type="text" name="bar" />
        <input type="submit" value="Save" />
    </form>
    
    // foo.php
    if (isset($_SESSION['token']))
    {
        if (isset($_POST['token']))
        {
            if ($_POST['token'] != $_SESSION['token'])
            {
                // double submit
            }
        }
    }
    
person Aron Rotteveel    schedule 25.01.2010
comment
+1 Отличный ответ, но я бы отредактировал, чтобы ваша рекомендация была первой, а затем вы следовали методам, которые вы не рекомендуете, - это облегчило бы чтение. Тем не менее, это должен быть принятый ответ. - person Peter Bagnall; 13.02.2014
comment
Для решения javascript вы можете создать эту функцию для события отправки формы, а не для нажатия кнопки отправки. Эта функция будет вызвана и отключит кнопку отправки. - person machineaddict; 16.09.2014
comment
Видно, что метод Три совершенен - person yip; 04.06.2015
comment
Я не уверен, что это правильно. если вы дважды нажмете «Отправить» в одной и той же форме, один и тот же токен будет отправлен дважды. следовательно, токен в сеансе необходимо сбросить после его использования. - person Himanshu Ranavat; 15.10.2017
comment
также смущен тем, почему повторная отправка формы будет генерировать другой токен во второй раз. Может кто-нибудь уточнить? - person Robert Sinclair; 30.07.2018
comment
Если кому-то интересно узнать, почему токен меняется, когда мы нажимаем «Отправить» во второй раз, пожалуйста, обратитесь к этому вопросу stackoverflow.com/questions/51599485/ - person Robert Sinclair; 30.07.2018

Используйте уникальный токен с сообщением, чтобы каждое сообщение/постбэк обрабатывалось только один раз.

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

person Mats Fredriksson    schedule 25.01.2010
comment
+1 - это наиболее распространенный метод, не зависящий от клиентской стороны. Почему это не принимается в качестве ответа? - person Russell; 11.03.2010
comment
@ Стив Неправда. Ожидается, что POST не будут идемпотентными. - person kaqqao; 10.03.2013
comment
Обратитесь к этому вопросу, если хотите узнать, почему токен меняется, когда мы нажимаем «Отправить» во второй раз. stackoverflow.com/questions/51599485/ - person Robert Sinclair; 30.07.2018

Создайте уникальный одноразовый ключ для отправки вместе с формой.

Полагаться на Javascript — плохая идея, потому что он может быть отключен пользователем в клиенте. С ключевой схемой, когда отправка получена сервером, отправка для этого ключа может быть заблокирована. Вы можете отвечать на повторяющиеся сообщения, как вам нравится.

Чтобы этот подход работал, ключи должны быть уникальными, и их очень трудно предсказать. В противном случае некоторые формы могут быть заблокированы из-за конфликтов клавиш. Таким образом, вам не нужно отслеживать ключи для каждой отправки формы и избегать конфликтов, срок действия ключей должен истекать вместе с сеансом этого пользователя. Еще одна вещь, на которую следует обратить внимание, это то, что если злоумышленники могут предсказать ключ, ваш код может быть уязвим для какого-либо взлома или эксплойта DOS.

person Dana the Sane    schedule 25.01.2010
comment
Это подход, который я использую. Создавайте GUID при каждом отображении страницы и включайте его в свою базу данных при каждом сохранении формы. В коде обработки отправки убедитесь, что GUID еще не существует в базе данных перед вставкой. - person 3Dave; 25.01.2010
comment
Какой тип алгоритма генерации GUID вы обычно используете? - person Dana the Sane; 25.01.2010

Предотвратить повторную отправку формы показывает способ сделать это без JavaScript.

person Javi    schedule 25.01.2010

Отключите кнопку отправки с помощью Javascript после отправки формы (onsubmit).

Обратите внимание, что если пользователь отключил Javascript, он все равно может отправить дважды. Происходит ли это достаточно часто, чтобы оправдать также проверку вашего кода (как вы предложили в своем вопросе), зависит от вас.

person Aistina    schedule 25.01.2010

В качестве альтернативы используйте одноразовые ключи формы.

person mst    schedule 25.01.2010

Я использую это:

$form_token = $_SESSION['form_token'] = md5(uniqid());

отправьте $form_token вместе с формой, затем...

Я проверяю form_token:

if($_SESSION['form_token'] != $_POST['form_token']) {
   echo 'Error';
}
person CSSJediEsck21    schedule 25.01.2010

отключите кнопку отправки, как только пользователь отправит форму, используя JavaScript. Это то, что, например, использует StackOverflow, когда вы отвечаете на вопрос.

Например

<input type="submit" onclick="this.disabled=true" />
person Marius    schedule 25.01.2010

Двойное нажатие отправки довольно редко приводит к множественным вставкам, но если вам нужно очень простое решение, используйте

onsubmit="document.getElementById('submit').disabled = true"

в теге формы, если вы дадите кнопку отправки id="submit"

person raveren    schedule 25.01.2010

Используйте JavaScript, чтобы отключить кнопку, как только она будет нажата.

onclick="this.disabled=true;forms[0].submit();"
person Joel Etherton    schedule 25.01.2010
comment
Обработчик onclick не запустится, если форма отправляется с помощью нажатия на клавиатуре, например. пользователь нажимает клавишу ввода, фокусируясь на кнопке отправки. Лучше используйте onsubmit, как предложил один из других плакатов. - person Asaph; 25.01.2010
comment
@Asaph: Мне действительно нравится идея токена, предложенная @Mats. Но если бы вы оставались на стороне клиента, на самом деле, вероятно, было бы лучше иметь метод, который включает в себя как щелчок, так и отправку. 2 щелчка мыши могут быть зарегистрированы в IE до того, как onsubmit действительно сработает. Глупо, но я видел это. - person Joel Etherton; 25.01.2010

Еще один способ создать страницу подтверждения, на которой пользователь, наконец, может нажать кнопку отправки. Это может уменьшить возможности такого рода.

person Diwakar Kumar Singh    schedule 20.07.2017

Я делаю следующее на action.php

if($_SESSION && isset($_SESSION['already_submitted'])) {

  # If already submitted just redirect to thank you page

} else {

    # If first submit then assign session value and process the form
    $_SESSION['already_submitted'] = true;

    # Process form

}

Примечание. Всегда инициируйте свои сеансы в заголовке, используя session_start();

person Robert Sinclair    schedule 30.07.2018

Мне помогло включение уникального токена в каждый POST Арона Роттевила. Однако моя форма по-прежнему отправляется, когда пользователь обновляет страницу (F5). Мне удалось решить это, добавив сброс:

            // reset session token
            $_SESSION['token'] = md5( $_POST['token'] . time() );

мои окончательные сценарии выглядят так:

if (isset($_SESSION['token'])) {
    if (isset($_POST['token'])) {
        if ($_POST['token'] != $_SESSION['token']) {
            echo '<h3>Oops! You already submitted this request.</h3>';
        } else {
            // process form
            // reset session token
            $_SESSION['token'] = md5( $_POST['token'] . time() );
        }
    } else {
        echo 'post token not set';
        $_SESSION['token'] = md5( $somevariable . time() );
    }
}

ой! не забудьте добавить session_start(); перед <!DOCTYPE>

Я новичок в PHP, поэтому, пожалуйста, поправьте меня, если вы обнаружите нарушения.

person Pabile    schedule 20.11.2011
comment
это бесполезный код. Просто выполните перенаправление HTTP после отправки формы. - person Your Common Sense; 20.11.2011
comment
Я хочу это сделать, но моя форма работает на одной странице. Сначала я хочу использовать jquery unload(), но я нахожу это полезным в тех случаях, когда пользователь случайно нажал f5 (перезагрузка страницы). у вас есть ссылка в этом редиректе? я хотел бы пересмотреть - person Pabile; 20.11.2011
comment
твоя причина не имеет смысла. вы также можете сделать перенаправление HTTP на одной странице. - person Your Common Sense; 20.11.2011
comment
см. этот мой ответ для примера ="печать встроенной ошибки php вместо стирания всей страницы"> stackoverflow.com/questions/7214860/ - person Your Common Sense; 20.11.2011

Используйте JavaScript, чтобы остановить его отправку

person Simon    schedule 27.01.2012
comment
Пример/демонстрационный код сделает этот ответ потенциально ценным. - person nickhar; 27.10.2012

person    schedule
comment
Пожалуйста, поделитесь контекстом вашего кода в деталях. Поскольку заданный вопрос не имеет контекста, по крайней мере, укажите свой :) - person Dinesh Suthar; 28.05.2021
comment
Просто выплевывать какой-то код недостаточно! Вы должны объяснить это тоже. См. stackoverflow.com/help/how-to-answer. - person biberman; 28.05.2021