5

I want to upload files from my angularJS client. I have enabled CSRF protection and it works fine except when i try to upload a file, where i get a 403 error:

Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-XSRF-TOKEN'.

But the token is in the request headers and is correct! When i disable the CSRF protection i can upload the files without any problem.

The CSRF protection works fine besides that.

Here is my current configuration:

SecurityConfiguration.java

@Configuration public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private SecurityUserDetailsService securityUserDetailsService; @Autowired private AuthFailureHandler authFailureHandler; @Autowired private AjaxAuthSuccessHandler ajaxAuthSuccessHandler; @Override protected void configure(HttpSecurity http) throws Exception { http .exceptionHandling() .authenticationEntryPoint(authFailureHandler) .and() .authorizeRequests() .antMatchers( "/", "/index.html", "/styles/**", "/bower_components/**", "/scripts/**" ).permitAll().anyRequest() .authenticated() .and().formLogin().loginPage("/login").permitAll() .and().logout().logoutUrl("/logout").logoutSuccessHandler(ajaxAuthSuccessHandler).permitAll() .and().httpBasic() .and().addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class) .csrf().csrfTokenRepository(csrfTokenRepository()); } private CsrfTokenRepository csrfTokenRepository() { HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository(); repository.setHeaderName("X-XSRF-TOKEN"); return repository; } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService(securityUserDetailsService) .passwordEncoder(new BCryptPasswordEncoder()); } } 

CsrfHeaderFilter.java

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); } } 

And last i have added a SecurityWebApplicationInitializer as pointed here.

SecurityWebApplicationInitializer.java

public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer { @Override protected void beforeSpringSecurityFilterChain(ServletContext servletContext) { insertFilters(servletContext, new MultipartFilter()); } } 

What am i doing wrong?

Update: i added this config but i still get a 403.

@Configuration public class MultipartUploadConfig { @Bean(name = "filterMultipartResolver") public MultipartResolver multipartResolver() { return new CommonsMultipartResolver(); } } 
5

3 Answers 3

2

A quick solution if you are OK with sending the CSRF Token as a url parameter. In your html template.

<form .... th:action="@{/upload(${_csrf.parameterName}=${_csrf.token})}"> ... </form> 
Sign up to request clarification or add additional context in comments.

2 Comments

@ChristosBaziotis: They are on answering spree -> same answer.
that's exposing the token in the URL, bad practice
0

If you use Ajax, put token parameter in url, to action in form action:

url: "/uploadFile?${_csrf.parameterName}=${_csrf.token}" 

It becomes like this:

function uploadFile() { $.ajax({ url: "/uploadFile?${_csrf.parameterName}=${_csrf.token}", type: "POST", data: new FormData($("#upload-file-form")[0]), enctype: 'multipart/form-data', processData: false, contentType: false, cache: false, success: function () { // Handle upload success $("#upload-file-message").text("File succesfully uploaded"); }, error: function () { // Handle upload error $("#upload-file-message").text( "File not uploaded (perhaps it's too much big)"); } }); } 

2 Comments

that's exposing the token in the URL, bad practice
With Ajax, you can just add it to the request headers, like explained here stackoverflow.com/questions/21514074/…
0

You can do the following to provide the token on each Ajax call:

var token = $("meta[name='_csrf']").attr("content"); var header = $("meta[name='_csrf_header']").attr("content"); $(document).ajaxSend(function(e, xhr, options) { xhr.setRequestHeader(header, token); }); 

There's another StackOverflow post that talks about this, that's where I found, but it's been a while since I saw it.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.