Избегайте условий гонки в PHP при отправке: Пожалуйста, не нажимайте "Отправить" более одного раза! - PullRequest
5 голосов
/ 06 января 2009

Некоторое время назад онлайн-приложения говорили: «Не нажимайте кнопку« Отправить »более одного раза». Это ушло сейчас, верно? Как вы защититесь от этого, скажем, в PHP?

Одно из решений, которое я использую, заключается в добавлении переменной в сеанс, поэтому вы не можете отправлять на страницу чаще, чем раз в 10 секунд. Таким образом, работа с базой данных будет завершена, чтобы можно было выполнять обычные проверки. Очевидно, это похоже на взлом и, вероятно, так.

Редактировать: Спасибо всем за решение Javascript. Это хорошо, но это немного работы. 1) Это тип ввода = изображение и 2) Подтверждение должно продолжаться до тех пор, пока Spry stuff не скажет, что все в порядке. Эта редакция, в основном, мне жаловалась, так как я представляю, что после просмотра материала Spry я смогу это выяснить.

Редактировать: Не то чтобы кто-то интегрировался с вещами Spry, но вот мой последний код, использующий Prototype для document.getElementByid. Комментарии приветствуются!

function onSubmitClick() {
    var allValid = true;
    var queue = Spry.Widget.Form.onSubmitWidgetQueue; 
    for (var i=0;i<queue.length; i++) {
        if (!queue[i].validate()) {
            allValid = false;
            break;
        }
    }

    if (allValid) {
        $("theSubmitButton").disabled = true;
        $("form").submit();
    }
}

По какой-то причине вторая форма была необходима ...

Ответы [ 8 ]

23 голосов
/ 28 марта 2009

Вы должны выполнять защиту как на стороне клиента, так и на стороне сервера.

Клиентская сторона - кнопка отключения, например, по словам Клетуса.

Серверная сторона - поставить токен в форме. Если есть два представления с одинаковым токеном, игнорируйте последний. Используя этот подход, вы защищены от CSRF .

8 голосов
/ 06 января 2009

Это отличный пример того, для чего полезен jQuery (вы можете сделать это в любом Javascript, хотя). Добавьте этот код:

$("form").submit(function() {
  $(":submit",this).attr("disabled", "disabled");
});

И он отключает кнопки отправки после однократного нажатия.

2 голосов
/ 30 марта 2009

Я думаю, что лучшим вариантом будет позволить PHP-скрипту сначала установить флаг в массиве сессии, чтобы указать, что он обрабатывает форму. Таким образом, второй запрос может быть настроен на ожидание до тех пор, пока исходный запрос не будет завершен (используйте сторону сервера ожидания вызовов в ожидании сброса флага).

Важно не слишком сильно мешать кнопке отправки и процессу, потому что природа сети связана с неопределенностью; пользователь может захотеть нажать второй раз, если ответа не пришло, и вы не хотите потерять его данные. Это может произойти, если первый запрос потерян, и вы отключили их единственный способ отправки (и, следовательно, хранения) данных.

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

2 голосов
/ 06 января 2009

Также важно отметить, что поведение PHP по умолчанию, если он обнаруживает, что пользователь «отменил» запрос (закрыв браузер, нажав «стоп» или, возможно, нажав «Отправить» второй раз), заключается в прекращении выполнения сценария. Это нежелательно, если вы делаете какую-то длительную транзакцию. Подробнее здесь .

2 голосов
/ 06 января 2009

Как уже отмечали другие, вы можете отключить кнопку. Мне больше нравятся проверки на стороне сервера - JS может быть отключен, пользователь может нажать обновить (хотя, если вы правильно используете POST, который выдаст предупреждение) и т. Д.

Вы можете добавить метку времени к форме и отследить ее в сеансе - требуется, чтобы метка времени POSTed была больше, чем отслеживаемая. Это предотвратит большинство двойных публикаций без заметного влияния на пользовательский интерфейс.

1 голос
/ 18 июня 2009

Я сделал простую версию этого с javascript, когда работал с ASP.NET AJAX, но он должен работать в любом случае, когда у вашей кнопки есть действительный идентификатор.

Я предпринимаю следующие шаги в событии onclick кнопки:

  1. Отключить кнопку, которая вызвала событие onclick
  2. Сохраните идентификатор кнопки в магической переменной closureId, к которой я могу обратиться позже через закрытие
  3. Используйте функцию setTimeout для выполнения динамически определенного обратного вызова после указанного количества миллисекунд (5000 мс = 5 секунд)
  4. В функции обратного вызова я могу сослаться на магию closureId и снова включить кнопку после истечения времени ожидания

Ниже приведена простая кнопка HTML, которую вы можете добавить в файл test.html для игры:

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

Заметьте, я называю эту переменную closureId «магией», потому что я не совсем понимаю, как она работает. Я только что понял, что вызов this.id не работает, потому что this является ссылкой на функцию тайм-аута, которая выполняет функцию динамического обратного вызова и не имеет какого-либо DOM-идентификатора.

Я не смог найти какой-либо другой способ получить ссылку на исходное событие (this.this.id не работает), но лексическая область видимости как-то позволяет мне по-прежнему обращаться к переменной closureId, как она была определена в то время оригинального нажатия кнопки.

Не стесняйтесь исправлять / комментировать, если вы знаете лучший способ!

1 голос
/ 06 января 2009

Одним из решений является немедленное отключение кнопки при нажатии с использованием Javascript. Очевидно, это зависит от того, включен ли JavaScript в браузере клиента.

Уловка на стороне сервера лучше, так как она будет распознавать условия гонки между несколькими окнами браузера, если пользователь дважды редактирует одну и ту же запись.

0 голосов
/ 06 января 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...