Разработчику. Сборник рецептов PHP
Задавайте вопросы

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

Вернуться назад

Задача

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

Решение

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

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

<?php
$unique_id = uniqid(microtime(),1);
...
?>
<input type="hidden" name="unique_id" value="<?php echo $unique_id; ?>">
</form>

Затем в процессе обработки ищите этот идентификатор:

$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 позволяют без проблем изменить данные. Тем не менее длинную случайную метку нельзя угадать простой заменой на другое целое число.

См. также

Документацию по функции uniqid() на http://www.php.net/uniqid.

Вернуться назад

Рейтинг@Mail.ru

Яндекс.Метрика

Индекс цитирования


Рейтинг Сайтов ДОСКИ.РУ