Защита от многократной отправки одной и той же формы

Задача
Необходимо помешать пользователям отправлять одну и ту же форму несколько раз.

Решение
Сгенерируйте уникальный идентификатор и сохраните эту метку в скрытом поле формы. Перед обработкой формы проверьте, не была ли эта метка уже представлена. Если нет, то можно продолжать, а если да, то надо сгенерировать ошибку.

Для создания формы применяется функция uniqid(), чтобы получить уникальный идентификатор:

$unique_id = uniqid(microtime(),1);
...
?>

Затем в процессе обработки ищите этот идентификатор:
$unique_id = $dbh->quote($_GET['unique_id']);
$sth = $dbh->query("SELECT * FROM database WHERE unique_id = $unique_id");
if ($sth->numRows()) {
// уже была представлена, выдаем ошибку
} else {
// работаем с данными
}

Обсуждение
Пользователи повторно отправляют форму по множеству причин. Часто это всего лишь ошибочный щелчок по кнопке мыши – двойной вместо одинарного. Пользователь может нажать кнопку Back своего броузера, чтобы отредактировать или повторно проверить информацию, а затем снова нажать кнопку Submit вместо кнопки Forward.


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

Решение предохраняет базу данных от переполнения слишком большим количеством копий одной и той же записи. С помощью генерации метки, размещаемой в форме, можно однозначно идентифицировать этот конкретный экземпляр формы, даже если cookies запрещены. Записывая впоследствии данные из формы, вы сохраняете вместе с ними
и эту метку. Это позволяет легко определить, не видели ли вы уже эту форму и запись в базе данных, связанную с ней.

Начните с добавления в таблицу базы данных дополнительной колонки unique_id, предназначенной для хранения идентификатора. При добавлении данных в запись вставьте также и идентификатор. Например:

$username = $dbh->quote($_GET['username']);
$unique_id = $dbh->quote($_GET['unique_id']);
$sth = $dbh->query("INSERT INTO members ( username, unique_id)
VALUES ($username, $unique_id)");

Ассоциируя правильную строку в базе данных с формой, можно легко обрабатывать повторную отправку.


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

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

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


Есть две (по крайней мере) проблемы с этим методом. Во-первых, он создает условия гонок. Что происходит, когда второй человек запускает форму до того, как первый человек закончит с ней работу?

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

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

Оцените статью: (0 голосов)
0 5 0

Статьи из раздела PHP на эту тему:
Использование элементов формы с несколькими вариантами значений
Кэширование запросов и результатов
Обработка внешних переменных с точками в именах
Обработка загруженных файлов
Обработка информации, полученной из формы

Вернуться в раздел: PHP / 9. Формы