The post has been translated automatically. Original language: Russian
The form on the website looks like a reliable boundary: the fields are limited, there is only one submit button, and you cannot enter anything superfluous. In fact, the border does not run there. You can send a request to the server directly, bypassing the entire page, and no restrictions on the page will stop this.
What does this mean in practice?
- Checking only on the page does not protect anything. It is necessary for the convenience of a person to suggest an error immediately. The real check must be on the server.
- Hidden fields and blocked buttons are not a limitation. The value of the hidden field is visible and changes in a few clicks.
- The price and discount coming from the form are a gift to the attacker. The server should take the price from its database, not from the request.
- The ID of someone else's record instead of your own. If the server does not check whether the record belongs to the sender, any user can open the data of another by the number in the address.
The fourth point occurs most often and is discovered later than everyone else, because in normal work no one substitutes other people's numbers.
What to always check on the server
- Type and format: a number is a number, a date is a date, and the length is within acceptable limits.
- Acceptable values: if there are three statuses, the fourth one cannot be accepted.
- Access right: whether this user has the right to this record and to this action.
- Semantic rules: the amount is greater than zero, the end date is later than the start date, the amount is not negative.
- Data size: text length, file weight, number of items in the list. Without a limit from above, a single request can take up all the memory.
Separately about files and text
The extension in the file name does not confirm anything. You need to check the contents, and it's better to replace the name with your own when saving. The text that is then shown to other users cannot be inserted into the page as it is, otherwise you can send a code through a comment that will be executed by others.
A simple rule
Anything that comes from outside is considered unchecked until the server checks it. Not just forms: the page address, request headers, responses from other services, and the contents of the uploaded file. The validation is closer to the data, not closer to the user.
It's not about distrust of people. Most errors in such places are found not by intruders, but by ordinary users who accidentally inserted an extra character or opened an old link.
Do you have data verification described in one place, or is it repeated in each handler in its own way?
Форма на сайте выглядит как надёжная граница: поля ограничены, кнопка отправки одна, лишнее ввести нельзя. На самом деле граница проходит не там. Запрос к серверу можно отправить напрямую, минуя страницу целиком, и никакие ограничения на странице этого не остановят.
Что это значит на практике
- Проверка только на странице ничего не защищает. Она нужна для удобства человека, чтобы подсказать ошибку сразу. Настоящая проверка обязана быть на сервере.
- Скрытые поля и заблокированные кнопки не ограничение. Значение скрытого поля видно и меняется в несколько кликов.
- Цена и скидка, приходящие с формы, это подарок злоумышленнику. Сервер должен брать цену из своей базы, а не из запроса.
- Идентификатор чужой записи вместо своего. Если сервер не проверяет, принадлежит ли запись отправителю, любой пользователь может открыть данные другого по номеру в адресе.
Четвёртый пункт встречается чаще всего и обнаруживается позже всех, потому что в обычной работе никто не подставляет чужие номера.
Что проверять на сервере всегда
- Тип и формат: число это число, дата это дата, длина в допустимых пределах.
- Допустимые значения: если статусов три, четвёртый принимать нельзя.
- Право доступа: есть ли у этого пользователя право на эту запись и на это действие.
- Смысловые правила: количество больше нуля, дата окончания позже даты начала, сумма не отрицательная.
- Размер данных: длина текста, вес файла, количество элементов в списке. Без ограничения сверху один запрос может занять всю память.
Отдельно про файлы и текст
Расширение в имени файла ничего не подтверждает. Проверять нужно содержимое, а имя при сохранении лучше заменить на своё. Текст, который потом показывается другим пользователям, нельзя вставлять в страницу как есть, иначе через комментарий можно передать код, который выполнится у других.
Простое правило
Всё, что пришло извне, считается непроверенным, пока сервер это не проверил. Не только формы: адрес страницы, заголовки запроса, ответы других сервисов, содержимое загруженного файла. Проверка ближе к данным, а не ближе к пользователю.
Это не про недоверие к людям. Большинство ошибок в таких местах находят не злоумышленники, а обычные пользователи, которые случайно вставили лишний символ или открыли старую ссылку.
А у вас проверка данных описана в одном месте или повторяется в каждом обработчике по-своему?