43
votes

I am using spring security along with java config

@Override
protected void configure(HttpSecurity http) throws Exception { 
    http
    .authorizeRequests()
    .antMatchers("/api/*").hasRole("ADMIN")
    .and()
    .addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class)
    .exceptionHandling()
    .authenticationEntryPoint(restAuthenticationEntryPoint)
    .and()
    .formLogin()
    .successHandler(authenticationSuccessHandler)
    .failureHandler(new SimpleUrlAuthenticationFailureHandler());

I am using PostMan for testing my REST services. I get 'csrf token' successfully and I am able to login by using X-CSRF-TOKEN in request header. But after login when i hit post request(I am including same token in request header that i used for login post request) I get the following error message:

HTTP Status 403 - Could not verify the provided CSRF token because your session was not found.

Can any one guide me what I am doing wrong.

7

7 Answers

80
votes

According to spring.io:

When should you use CSRF protection? Our recommendation is to use CSRF protection for any request that could be processed by a browser by normal users. If you are only creating a service that is used by non-browser clients, you will likely want to disable CSRF protection.

So to disable it:

@Configuration
public class RestSecurityConfig extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable();
  }
}

Note: CSRF protection is enabled by default with Java Configuration

4
votes

Disabling CSRF protection is a bad idea.

Spring will automatically generate a new CSRF token after each request, and you need to include it in all HTTP requests with side-effects (PUT, POST, PATCH, DELETE).

In Postman you can use a test in each request to store the CSRF token in a global, e.g. when using CookieCsrfTokenRepository

pm.globals.set("xsrf-token", postman.getResponseCookie("XSRF-TOKEN").value);

And then include it as a header with key X-XSRF-TOKEN and value {{xsrf-token}}.

4
votes

try this: @Override protected boolean sameOriginDisabled() { return true;}

@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {

    ...

    // Determines if a CSRF token is required for connecting. This protects against remote
    // sites from connecting to the application and being able to read/write data over the
    // connection. The default is false (the token is required).
    @Override
    protected boolean sameOriginDisabled() {
        return true;
    }
}

source: WebSocket Security: Disable CSRF within WebSockets

2
votes

Came to same error just with POST methods, was getting 403 Forbidden "Could not verify the provided CSRF token because your session was not found."

After exploring some time found solution by adding @EnableResourceServer annotation to config.

Config looks like that (spring-boot.version -> 1.4.1.RELEASE, spring-security.version -> 4.1.3.RELEASE, spring.version -> 4.3.4.RELEASE)

@Configuration
@EnableWebSecurity
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends ResourceServerConfigurerAdapter {

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
  auth.userDetailsService(inMemoryUserDetailsManager()).passwordEncoder(passwordEncoder());
}

@Override
public void configure(HttpSecurity http) throws Exception {
    http.httpBasic();
    http.sessionManagement().sessionCreationPolicy(STATELESS);
    http.csrf().disable();
    http.authorizeRequests().anyRequest()
            .permitAll();
}

private InMemoryUserDetailsManager inMemoryUserDetailsManager() throws IOException {
    // load custom properties
    Properties properties = new Properties();
    return new InMemoryUserDetailsManager(properties);
}

private PasswordEncoder passwordEncoder() {
    return new TextEncryptorBasedPasswordEncoder(textEncryptor());
}

private TextEncryptor textEncryptor() {
    return new OpenSslCompatibleTextEncryptor();
}

}
0
votes

I get this error message (HTTP Status 403 - Could not verify the provided CSRF token because your session was not found.) when I do a JS fetch AJAX call without using the credentials: "same-origin" option.

Wrong way

fetch(url)
.then(function (response) { return response.json(); })
.then(function (data) { console.log(data); })

Correct way

fetch(url, {
    credentials: "same-origin"
})
.then(function (response) { return response.json(); })
.then(function (data) { console.log(data); })
0
votes

This is an old question but this might help someone. I had the similar issue and this is how I was able to resolve it.

In order for the CSRF to work with the REST API you need to obtain a CSRF token via API before every single call and use that token. Token is different every time and cannot be re-used.

Here is the controller to get the CSRF token:

@RequestMapping(value = "/csrf", method = RequestMethod.GET)
    public ResponseEntity<CSRFDTO> getCsrfToken(HttpServletRequest request) {
        CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
        return ResponseEntity.ok(CSRFDTO.builder()
                .headerName(csrf.getHeaderName())
                .token(csrf.getToken())
                .build());
    }

Additionally, you might consider configuring your Spring app to disable the CSRF for the REST API endpoints. To quote an article I've read somewhere:

I'm very certain that CSRF tokens on a REST endpoint grant zero additional protection. As such, enabling CSRF protection on a REST endpoint just introduces some useless code to your application, and I think it should be skipped.

Hope this helps.

-4
votes

I have solved it by adding the last attribute in my login page,maybe it will do yo a favor.

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"  isELIgnored="false"%>