📚 Теория: Сервисный guard и негативные тесты (раскрыть)
Почему опасно доверять ID?
Сервис загружает профиль напрямую через репозиторий. Контроллер уже прошёл аутентификацию, значит любой ID вернёт данные без проверки владельца.
⚠️ Нет проверки tenant/subject — классический IDOR.
Что делать?
Вынесите проверку во все сервисы: requireOwnedProfile(id, principal) + AuthorizationManager. Закройте сценарии тестами.
🔐 Негативные тесты блокируют регрессии при refactor.
Атакующий с токеном USER просит /api/v1/users/100.
Метод возвращает профиль ID 100 без проверки владельца.
Личные данные уходят, а журнал воспринимает это как легитимный запрос.
Как надо: добавьте guard в сервис, используйте SpEL на контроллерах, в тестах покрывайте 200/403, а в аудиторе помечайте попытки доступа к чужим ресурсам.
Контекст
Перед релизом убрали проверку владельца из сервиса, чтобы «переиспользовать» метод в админке. Тесты не добавили, так что регрессия осталась незамеченной. Теперь любой авторизованный пользователь может читать чужой профиль.
- Найди точку, где сервис отдаёт профиль без конвейера авторизации.
- Подумай о паттерне: централизованный guard, ACL, атрибуты в токене.
- Пропиши в комментарии набор тестов на 403 с чужими токенами.
Сниппет
Code Review
Референс-исправление
Guard должен жить в сервисе: проверяем владельца, роль, тенанта и пишем негативные тесты.
Флаг ревью: FLAG{java-idor-service-guard}
public UserProfile requireOwnedProfile(Long id, AppUser principal) {
UserProfile profile = repository.findByIdAndOwnerId(id, principal.getId())
.orElseThrow(() -> new AccessDeniedException("user mismatch"));
accessManager.check(principal, Permissions.PROFILE_READ, profile);
return profile;
}
В тестах используй mockMvc.perform(get(\"/api/v1/users/42\").with(jwt().jwt(jwt -> jwt.subject(\"mallory\")))) и проверяй статус 403.
Проверка флага
После ревью зафиксируй флаг, чтобы дашборд засчитал лабораторию.