Я пытался добавить защиту CSRF / XSRF в свое приложение, но столкнулся со странным поведением. Все запросы на получение работают нормально, но на все посты / вставки / удаления я получаю 403 Unauthorized. И самое странное, что когда я пытался отладить мой CSRF-фильтр, запросы не доходят до него, они отклоняются где-то раньше. Они даже не доходят до моего фильтра аутентификации, поэтому я не могу понять, в чем проблема.
Мой конфиг безопасности:
@Override
public void configure(HttpSecurity http) throws Exception {
http
...
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService()), UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
.csrf().csrfTokenRepository(csrfTokenRepository());
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
Я не добавляю фильтры, поскольку, как я уже сказал, запросы не доходят до них. Но при необходимости я закончу свой вопрос. Надеюсь на вашу помощь, заранее спасибо!
3 ответа
Большое спасибо за ответы, они действительно помогли мне найти решение. И я хочу поделиться своим решением, если в будущем кто-то столкнется с той же проблемой.
Как отмечалось в ответах, я использовал SessionCreationPolicy.STATELESS
и не имел сессий, поэтому вместо HttpSessionCsrfTokenRepository
мне пришлось использовать CookieCsrfTokenRepository
с withHttpOnlyFalse()
, чтобы AngularJS мог читать куки.
В результате у меня есть такая конфигурация:
@Override
public void configure(HttpSecurity http) throws Exception {
http
...
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService()), UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
.csrf().csrfTokenRepository(csrfTokenRepository());
}
Если кого-то интересует, как выглядит CsrfHeaderFilter:
public class CsrfHeaderFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie==null || token!=null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
}
Моя вторая проблема была CORS. Документация AngularJS гласит:
«Заголовок не будет установлен для междоменных запросов».
Чтобы решить эту проблему, мне пришлось использовать HTTP-перехватчик:
.factory('XsrfInterceptor', function ($cookies) {
return {
request: function (config) {
var headerName = 'X-XSRF-TOKEN';
var cookieName = 'XSRF-TOKEN';
config.headers[headerName] = $cookies.get(cookieName);
return config;
}
};
});
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('XsrfInterceptor');
}]);
Я надеюсь, что мой ответ будет полезен.
В принципе, механизм CSRF в Spring хранит токен CSRF в файле cookie только HTTP. Поскольку JavaScript не может получить доступ только к файлам cookie HTTP, необходимо указать Spring, чтобы отключить только HTTP:
.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
Затем вы можете прочитать файл cookie из Angular и добавить его в заголовок XSRF-TOKEN при каждом запросе.
Это общий случай. Я не уверен, подходит ли это вашему особому случаю.
Предполагая, что остальные ваши конфиг / фильтры работают правильно, вы столкнулись с этой проблемой из-за этого: SessionCreationPolicy.STATELESS
.
Вы можете заглянуть под капот Spring ({X0}}). Вы увидите, что он должен помнить значение каждого CSRF-токена для каждого пользователя в сеансе, и, поскольку вы не используете сеансы, это невозможно сделать.
Что делать дальше - действительно решать вам. Некоторые люди говорят, что если ваше приложение не имеет состояния, на самом деле нет необходимости в защите CSRF. Spring Docs говорит, что CSRF-атаки все еще актуальны. Я думаю, что это действительно зависит от вашего механизма аутентификации.
Вы также можете посмотреть на Это хорошая статья, например.
Надеюсь, это поможет.
Похожие вопросы
Новые вопросы
angularjs
Используйте для вопросов об AngularJS (1.x), JavaScript-фреймворке с открытым исходным кодом. НЕ используйте этот тег для Angular 2 или более поздних версий; вместо этого используйте тег [angular].