Авторизация и контроль доступа
Аутентификация доказывает, кто ты. Авторизация решает, что тебе можно трогать. Самая частая и самая разрушительная ошибка в современных приложениях - неправильная вторая часть: залогиненный пользователь читает или меняет данные, которые ему не принадлежат.
Чек-лист
- Проверяй владение при каждом доступе к объекту. Для каждого маршрута, который принимает id (счёт, заметка, проект, сообщение), убедись, что текущий пользователь владеет этой конкретной записью или имеет к ней доступ, прежде чем вернуть или изменить её. Это Broken Object Level Authorization (BOLA), также известная как IDOR, и это риск номер один по OWASP.
- Фильтруй по владельцу, а не только по id. Запрос вида
SELECT * FROM invoices WHERE id = ?- это IDOR. СделайWHERE id = ? AND owner_id = ?и возвращай 404 при промахе. - Не полагайся на клиент в том, чтобы что-то скрывать. Скрытая кнопка или отсутствующая ссылка - это не контроль доступа. Проверка должна быть на сервере.
- Сохраняй авторизацию при рефакторинге. Проверки доступа, добавленные в одном изменении, часто теряются в следующем. Перепроверяй владение после каждого изменения маршрутов данных.
- Включай row-level security в базе данных, если платформа это поддерживает, чтобы забытая проверка в коде всё равно ловилась на уровне данных.
- Закрывай каждый админ-маршрут и внутренний маршрут. Убедись, что вызывающий действительно админ из доверенного источника (серверная роль, а не поле, которое пользователь может задать), и держи внутренние маршруты недоступными из публичного интернета.
- Изолируй арендаторов. В мультитенантном приложении каждый запрос и каждый путь к файлу должны ограничиваться тенантом вызывающего через одну точку контроля, чтобы один клиент никогда не получил доступ к данным или файлам другого.
- Ограничивай пути к файлам. Когда запрос называет файл, разреши путь и убедись, что он остаётся внутри разрешённой директории. Проверка должна останавливаться на разделителе пути, чтобы соседняя папка с более длинным именем не была доступна.
Как тестировать
- Создай два аккаунта. От имени аккаунта A запроси id объектов аккаунта B напрямую. Ничего из данных B не должно вернуться.
- Вызови админ-маршрут токеном обычного пользователя. Ожидай 403.
- Запроси пути вида
../и пути с соседним префиксом на любом файловом эндпоинте.