middle
Как обеспечить безопасность REST API?
Безопасность REST API — комплексная задача, включающая несколько уровней защиты: от транспортного шифрования до аудита действий.
Чек-лист безопасности REST API
| Уровень | Меры |
|---|---|
| Транспорт | HTTPS (TLS) для всех endpoint-ов |
| Аутентификация | JWT / OAuth 2.0 / API Key |
| Авторизация | Ролевая модель (@PreAuthorize) |
| Валидация | Все входные данные (@Valid) |
| Rate Limiting | Защита от DDoS и brute-force |
| CORS | Только доверенные домены |
| DTO | Вместо сущностей в API (защита от Mass Assignment) |
| Заголовки | X-Content-Type-Options, X-Frame-Options, HSTS |
| Информация | Нет утечки стек-трейсов, версий, внутренних ID |
| Аудит | Логирование аутентификации, авторизации, изменений |
Защита от массового присваивания (Mass Assignment)
Пример
// Опасно — клиент может передать {"role": "ADMIN"}
@PostMapping
public User create(@RequestBody User user) { ... }
// Безопасно — DTO содержит только допустимые поля
@PostMapping
public UserDto create(@RequestBody CreateUserRequest request) { ... }
Примеры конфигурации Spring Security
Авторизация на уровне методов:
@PreAuthorize("hasRole('ADMIN')")
@DeleteMapping("/users/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) { ... }
@PreAuthorize("hasRole('USER') and #id == authentication.principal.id")
@GetMapping("/users/{id}/profile")
public ResponseEntity<Profile> getProfile(@PathVariable Long id) { ... }
Защита заголовков:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.headers(headers -> headers
.contentTypeOptions(Customizer.withDefaults()) // X-Content-Type-Options: nosniff
.frameOptions(frame -> frame.deny()) // X-Frame-Options: DENY
.httpStrictTransportSecurity(hsts -> hsts
.maxAgeInSeconds(31536000)
.includeSubDomains(true))) // Strict-Transport-Security
.build();
}
Минимизация раскрытия информации:
server:
error:
include-stacktrace: never
include-message: never
Аудит:
@Aspect
@Component
@Slf4j
public class AuditAspect {
@AfterReturning("@annotation(auditable)")
public void audit(JoinPoint joinPoint, Auditable auditable) {
String user = SecurityContextHolder.getContext()
.getAuthentication().getName();
log.info("Audit: user={}, action={}, method={}",
user, auditable.action(), joinPoint.getSignature().getName());
}
}
На собеседовании: нужно перечислить минимум 5-6 уровней защиты из чек-листа. Частая ошибка — говорить только про аутентификацию и забыть про валидацию, CORS, Rate Limiting и DTO.