Как передавать userId в запросах для получения аккаунтов
Что проверяют
Понимаете ли вы, что для получения своих аккаунтов userId от клиента вообще не нужен: сервер знает пользователя из аутентификации. Вопрос ловит уязвимость IDOR, в OWASP API Security она называется Broken Object Level Authorization. Следом спрашивают, как тогда администратор посмотрит аккаунты другого пользователя.
Как отвечать
Разделите два сценария.
Свои аккаунты: эндпоинт вида GET /me/accounts без идентификатора. В Spring Security пользователь берётся из контекста: @AuthenticationPrincipal Jwt jwt и jwt.getSubject() или SecurityContextHolder.getContext().getAuthentication(). Resource Server проверяет подпись токена, поэтому подменить sub клиент не может.
Чужие аккаунты: идентификатор в пути, GET /users/{userId}/accounts, и обязательная проверка права:
- на уровне метода:
@PreAuthorize("hasRole('ADMIN') or #userId == authentication.name"), в Spring Security 6 для этого нужна@EnableMethodSecurity; - на уровне данных: запрос с условием владельца, например
findByIdAndOwnerId, чтобы ошибка в аннотации не открыла чужие данные; - ответ при отказе: 403, а для чужих ресурсов часто 404, чтобы не подтверждать их существование.
Про остальные варианты коротко. Query-параметр оседает в логах прокси и истории браузера. Заголовок вроде X-User-Id допустим только между внутренними сервисами, когда его ставит шлюз после проверки токена и вырезает такой же заголовок из внешних запросов. Тело у GET-запроса не используют.
Где спотыкаются
Принимают userId из запроса и верят ему. Любой пользователь меняет число в URL и видит чужие счета, это классическая находка на пентестах.
Считают защитой UUID вместо последовательного id. Угадать сложнее, но идентификаторы утекают через ссылки и логи, а проверки прав всё равно нет.
Генерируют JWT на клиенте или путают аутентификацию с авторизацией: токен говорит, кто пришёл, но не отвечает, можно ли ему смотреть конкретный аккаунт.