Клиент отправляет запрос на изменение профиля с полем роли, которое отсутствует в интерфейсе. Какой механизм защиты должен предотвратить несанкционированное изменение этого поля?
От несанкционированного изменения защищает явное разрешение изменяемых полей на стороне сервера — список допустимых атрибутов для каждой операции. Сервер не должен автоматически связывать все поля входного объекта с моделью пользователя: поле роли нужно игнорировать или отклонять независимо от того, присутствует ли оно в интерфейсе.
Проблема возникла из-за удобного автоматического связывания входных данных с объектами предметной области. Такой механизм ускоряет разработку форм и API, но переносит доверие к структуре запроса на клиента, который полностью контролирует отправляемые данные.
Для снижения риска появились подходы allowlist и DTO: приложение заранее описывает поля, допустимые в конкретной операции, вместо попытки перечислить опасные поля после появления уязвимости.
Пользователь может вручную добавить в запрос поле, скрытое от обычного интерфейса. Если сервер автоматически передаст все полученные атрибуты в модель, злоумышленник сможет изменить роль, статус подтверждения, владельца объекта или другой внутренний признак.
Последствия зависят от модели данных: от обхода бизнес-ограничений до получения административных прав. Проверка только клиентского интерфейса, схемы формы или типа значения не защищает сервер, поскольку запрос можно сформировать независимо от браузерного интерфейса.
Сервер должен преобразовать входной запрос в отдельную структуру данных для конкретной операции. Для обновления профиля в неё входят, например, отображаемое имя и контактные данные, но не роль, идентификатор владельца или признак подтверждения учётной записи.
Безопаснее использовать разрешённый список полей и отклонять неизвестные поля либо явно сообщать о них об ошибке. Молчаливое игнорирование снижает риск случайного изменения, но может скрыть ошибку клиента; отклонение повышает обнаруживаемость, однако требует совместимости при эволюции API.
Поле, влияющее на полномочия, должно изменяться отдельной операцией с собственной проверкой авторизации. Даже если поле исключено из обычного обновления профиля, это не отменяет проверки права на его изменение в административном сценарии.
Тестирование должно отправлять дополнительные поля напрямую, проверять состояние объекта после ответа сервера, учитывать вложенные структуры, разные HTTP-методы и варианты сериализации. Важно убедиться не только в отсутствии успешного ответа, но и в том, что значение действительно не изменилось.
В API обновления профиля пользователь мог передавать имя и часовой пояс. Тестировщик добавил в запрос поле роли, после чего получил успешный ответ, а при следующем входе учётная запись получила административные возможности. Причиной оказалось автоматическое связывание всех атрибутов запроса с моделью пользователя.
Рассматривались два варианта. Первый — запретить несколько известных чувствительных полей: это быстро, но список легко устаревает при добавлении новых атрибутов. Второй — создать отдельную структуру допустимых полей для операции: это требует больше поддержки, зато новые внутренние атрибуты не становятся изменяемыми случайно.
Выбрали второй вариант, добавили отдельный административный маршрут с проверкой полномочий и негативные тесты на неизвестные поля. В результате обычное обновление стало ограничено пользовательскими атрибутами, а изменение роли стало доступно только через контролируемый административный сценарий.
Нет. Клиентский интерфейс и документация не являются границей безопасности: запрос можно изменить вручную. Защита должна применяться при разборе запроса на сервере до записи данных в модель или хранилище.
Проверка формата отвечает на вопрос, корректно ли значение с технической точки зрения. Она не отвечает на вопрос, имеет ли данный пользователь право менять это поле. Значение может быть синтаксически корректным, но изменение роли всё равно должно быть запрещено для обычной операции.
Такой подход является denylist и зависит от полноты текущего перечня исключений. При добавлении нового внутреннего атрибута разработчик может забыть включить его в список запрещённых, после чего он автоматически станет доступен для изменения. Allowlist безопаснее по умолчанию: новое поле не изменяется, пока его явно не разрешили для конкретной операции.