CSRF и Spring Security

CSRF заставляет браузер авторизованного пользователя отправить изменяющий запрос на чужой сайт. Атака работает, когда браузер автоматически добавляет credential: session cookie, Basic auth или client certificate.

пользователь вошёл в bank.example
        ↓ cookie хранится в браузере
пользователь открыл evil.example
        ↓ форма отправляет POST на bank.example
браузер сам добавляет cookie

Same-origin policy мешает прочитать ответ, но не всегда мешает отправить запрос.

Механизм token

Сервер выдаёт непредсказуемый CSRF token, связанный с пользовательским контекстом. Клиент возвращает его в параметре формы или заголовке. Чужой сайт не знает token, поэтому проверка отклоняет запрос.

В современной servlet-конфигурации Spring Security CSRF-защита включена по умолчанию:

@Bean
SecurityFilterChain security(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/public/**").permitAll()
            .anyRequest().authenticated()
        )
        .formLogin(Customizer.withDefaults())
        .csrf(Customizer.withDefaults());
 
    return http.build();
}

HTML form должна отправить token. Интеграция Spring MVC/template engine может добавить hidden field; SPA обычно получает token выбранным repository-механизмом и отправляет заголовок.

Когда отключение обосновано

Для API, где браузер не использует автоматически прикрепляемые credentials, а клиент вручную передаёт bearer token в Authorization, классический CSRF-риск обычно отсутствует. Но это надо доказать для всех endpoints. Если тот же API принимает cookie или Basic auth, защита снова нужна.

http.csrf(csrf -> csrf.disable()); // только после зафиксированной threat model

Отключение CSRF не защищает от XSS, кражи токена, CORS-ошибок или неверной авторизации. SameSite cookie — дополнительный слой, а не универсальная замена token.

Ошибки

  • отключить CSRF, потому что «это REST»;
  • применять token только к одному state-changing методу;
  • разрешить GET менять состояние;
  • логировать token;
  • выставить cookie token доступным JavaScript без оценки XSS.

Проверка

Тест должен подтвердить обе стороны: запрос без token отклоняется, с корректным token проходит. В spring-security-test для MockMvc используется request post-processor csrf().

На собеседовании

Почему cookie-аутентификация уязвима? Браузер автоматически прикладывает cookie к запросу на целевой origin.

CSRF token аутентифицирует пользователя? Нет, он подтверждает, что запрос сформирован в допустимом контексте.

Нужно ли защищать GET? GET не должен менять состояние; обычно token проверяют для unsafe HTTP methods.

Когда stateless API может отключить CSRF? Когда ни один поддерживаемый credential не прикладывается браузером автоматически и это закреплено архитектурой.

Связано: Безопасность приложения Spring Security, Spring WEB, HTTP протокол.