I have a resource service behind Cloud Gateway route with RelayToken filter:
routes:
- id: apis
uri: http://rest-app:8080/apis
predicates:
- Path=/apis/**
filters:
- TokenRelay=
GET requests work fine, but on POSTs I get 403 Forbidden with response body containing
CSRF Token has been associated to this client
I've tried to disable CSRF protection adding Bean
#Bean
fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http.csrf().disable().cors().disable().build()
}
But this has no effect and I still get 403. Moreover I cannot debug which exactly filter prevents client from doing POST requests, the only logging information I get with
logging:
level:
root: INFO
org.springframework.web: TRACE
org.springframework.security: TRACE
org.springframework.security.oauth2: TRACE
org.springframework.cloud.gateway: TRACE
org.springframework.security.jwt: TRACE
is just couple of lines saying POST was forbidden
[2020-04-01 13:21:32,635] TRACE o.s.w.s.a.HttpWebHandlerAdapter - [58a0e540-10] HTTP POST "/apis/", headers={masked}
[2020-04-01 13:21:32,640] TRACE o.s.w.s.a.HttpWebHandlerAdapter - [58a0e540-10] Completed 403 FORBIDDEN, headers={masked}
[2020-04-01 13:21:32,640] TRACE o.s.h.s.r.ReactorHttpHandlerAdapter - [58a0e540-10] Handling completed
How do I correctly turn CSRF off?
Correct SecurityWebFilterChain that solved my problem:
#Bean
fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http
.authorizeExchange().anyExchange().authenticated()
.and()
.oauth2Login()
.and()
.csrf().disable()
.build()
}
Related
The following spring security config gives some unexpected behavior.
When making a request to some (non-health-check) endpoint (/user), in the browser and when using curl (via git bash on windows), an unauthenticated request returns an idp redirect as expected.
However, when using the WebTestClient, it returns 401 Unauthorized with www-authenticate: [Basic ...].
The request for basic authn in this context (and the password generated at startup) are unexpected because I've declared to disable basic authn via http.httpBasic().disable().
Why would this response come? Is there a better way to override the default basic auth configs? Is there an ordering on these configurations as suggested in this post? Where is this documented?
...env values
#Bean
public SecurityWebFilterChain webFilterChain(ServerHttpSecurity http) {
http.oauth2Client()
.and()
.oauth2Login()
.and()
.httpBasic()
.disable()
.formLogin()
.disable()
.csrf()
.disable()
.authorizeExchange()
.pathMatchers("/actuator/health").permitAll()
.anyExchange().authenticated();
return http.build();
}
#Bean
ReactiveClientRegistrationRepository getClientRegistrationRepository() {
ClientRegistration google =
ClientRegistration.withRegistrationId("google")
.scope("openid", "profile", "email")
.clientId(clientId)
.clientSecret(clientSecret)
.authorizationUri(authUri)
.tokenUri(tokenUri)
.userInfoUri(userInfoUri)
.redirectUri(redirectUri)
.jwkSetUri(jwksUri)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.userNameAttributeName("name")
.build();
return new InMemoryReactiveClientRegistrationRepository(google);
}
Project on github: https://github.com/segevmalool/spring-samples/blob/main/spring-security-webflux-postgres
httpBasic().authenticationEntryPoint(new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED))
Solution
I override configure(HttpSecurity http) method in SampleSecurityConfig Class like this
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/delete/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin().and().httpBasic();
}
If i don't use httpBasic method, it seems no problem occurred.
what does httpBasic method exactly do?
Calling this method on HttpSecurity will enable Http Basic Authentication for your application with some "reasonable" defaults.
It will return a HttpBasicConfigurer for further customization.
You can test this by curl and passing a header like Authorization: Basic bzFbdGfmZrptWY30YQ== but base64 encoding a valid username/password combination.
Documentation for httpBasic
What are we saying by calling httpBasic() ?
When httpBasic() is called, we are telling Spring to authenticate the request using the values passed by the Authorization request header. If the request is not authenticated you will get a returned status of 401 and a error message of Unauthorized
What is actually happening when httpBasic() is called ?
By calling httpBasic(), an instance of the BasicAuthenticationFilter is added to the filter chain. The BasicAuthenticationFilter will then proceed with trying to authenticate the request in the typical Spring Security fashion. If authentication is successful, the resulting Authentication object will be placed into the SecurityContextHolder, which can then be used for future authentication purposes.
The official Spring Github Repo's Readme reads:
The application is almost finished functionally. The last thing we
need to do is implement the logout feature that we sketched in the
home page. If the user is authenticated then we show a "logout" link
and hook it to a logout() function in the AppComponent. Remember, it
sends an HTTP POST to "/logout" which we now need to implement on the
server. This is straightforward because it is added for us already by
Spring Security (i.e. we don’t need to do anything for this simple use
case). For more control over the behaviour of logout you could use the
HttpSecurity callbacks in your WebSecurityAdapter to, for instance
execute some business logic after logout.
Taken from: https://github.com/spring-guides/tut-spring-security-and-angular-js/tree/master/single
However, I am using basic authentication and testing it with Postman app. The POST on '/logout' gives me a 403 Forbidden like so:
{
"timestamp": "2018-07-30T07:42:48.172+0000",
"status": 403,
"error": "Forbidden",
"message": "Forbidden",
"path": "/logout"
}
My Security Configurations are:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/user/save")
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/user/**")
.hasRole("USER")
.and()
.authorizeRequests()
.antMatchers("/admin/**")
.hasRole("ADMIN")
.and()
.httpBasic()
.and()
.logout()
.permitAll()
.and()
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
I want the session to be invalidated, all the cookies to be deleted, such that when I query again on the endpoint /user with wrong credentials, I should get a 403. However, even after POST on /logout (which gives 403 anyway), the application accepts the GET on /user from the previous session and shows me the details of the user.
The endpoint is:
#GetMapping
public Principal user(Principal user){
return user;
}
I am experimenting with Spring Security 3.2.0.RC2 using javaconfig and it appears that the logout url is POST only. Is this by design and is there any way to make it logout a user with a GET request?
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**", "/signup", "/about", "/password").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated().and()
.formLogin()
.loginPage("/login")
.permitAll();
}
This is intentional and is documented within the CSRF documentation. The reason is to prevent CSRF attacks that forcibly log users out of your application. If you would like to support non-POST requests you can do so with the following Java Configuration:
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
}
You can also find information about configuring log out on the Javadoc of the LogoutConfigurer (i.e. the object returned by the http.logout() method).
I am pretty new to Spring Security land. I am using programmatic configuration of Spring Security with servletApi() which is pretty neat.
Here is the configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.securityContext().and()
.servletApi().and()
.authorizeUrls()
.antMatchers("/login").permitAll()
.antMatchers("/").permitAll()
.antMatchers("/**").authenticated();
}
I am using http servlet api login I am not using any filter for this.
In case a unauthorised request, ExceptionTranslationFilter uses Http403EntryForbiddenEntryPoint to return 403 forbidden status.
In my scenario:
If user does not authenticated, a 401 status code should return.
If user authenticated but not authorised, a 403 status code should return.
But default configuration creates 403 status for both case.
Here are my questions:
Why is the default entry point is Http403EntryForbiddenEntryPoint? It can be 401?
If I change Http403EntryForbiddenEntryPoint to Http401EntryForbiddenEntryPoint, does It create a problem?
Thanks