I have a Spring WebFlux security as follows and would like to control CSRF using property. How can I add if check for the CSRF alone here?
#Bean
public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {
return http.authorizeExchange().matchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
//.pathMatchers("/register", "/login").permitAll()
.anyExchange().authenticated()
.and().formLogin()
.securityContextRepository(securityContextRepository())
.and()
.exceptionHandling()
.accessDeniedHandler(new HttpStatusServerAccessDeniedHandler(HttpStatus.BAD_REQUEST))
.and().csrf().disable()
.build();
}
you just add something like:
// All your stuff up here then
if(!csrfEnabled) {
http.csrf().disable();
}
return http.build();
Related
We have migrated to Spring Boot 3 and Spring Security 6. The behavior of users who are not authenticated has changed.
Unauthenticated users should have Anonymous Authetification as before. Instead we get an AuthenticationException.
Is this behavior correct in the new version of Spring Security 6 or do we need to adjust our WebSecurityConfig?
Here is our filter chain:
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
log.debug("Configuring HTTP Security");
http
.csrf().disable()
.cors()
.and()
.headers()
.frameOptions().disable()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.and()
.authorizeHttpRequests()
.requestMatchers("/sf/**").permitAll()
.requestMatchers(HttpMethod.GET, "/health").permitAll()
.requestMatchers(HttpMethod.GET, "/metrics").permitAll()
.requestMatchers(HttpMethod.GET, "/error").permitAll()
.requestMatchers(HttpMethod.GET, "/favicon.ico").permitAll()
.requestMatchers(HttpMethod.GET, "/info").permitAll()
.requestMatchers("/").permitAll()
.requestMatchers("/**").authenticated()
.and()
.addFilterBefore(new RolesToRightsConverterFilter(s3RSpringConfig), BasicAuthenticationFilter.class)
.addFilterAfter(new Slf4jMDCFilter(authService, tracingService), RolesToRightsConverterFilter.class)
.oauth2ResourceServer().jwt().jwtAuthenticationConverter(new AadJwtBearerTokenAuthenticationConverter());
return http.build();
}
I have a Spring Security configuration like this:
#Override
public void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/**").hasAuthority("Business_User")
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/login")
.usernameParameter("username")
.passwordParameter("password");
http.headers().frameOptions().disable();
}
The authentication works fine. However, if an authentication failed because of either invalid user or wrong password, a standard Spring login page is returned and the return code is 200.
This is not what I expect.
How I can config Spring Security to return 401 and an empty response body if the user failed login?
You can do this if authentication failed spring security handle it by redirecting you to the login page but you can manage it by adding the following code:
http
.exceptionHandling()
.authenticationEntryPoint(new org.springframework.boot.autoconfigure.security.Http401AuthenticationEntryPoint("headerValue"));
or
you can also use HttpStatusEntryPoint like this
http
.exceptionHandling()
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
I'm trying to implement CSRF token security in my Spring Boot API to learn how to deal with that.
I've followed this tutorial (server side part) and this is my security config:
private static final String[] CSRF_IGNORE = {"/api/login"};
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.ignoringAntMatchers(CSRF_IGNORE)
.csrfTokenRepository(csrfTokenRepository())
.and()
.addFilterAfter(new CustomCsrfFilter(), CsrfFilter.class)
.exceptionHandling()
.authenticationEntryPoint(new Http403ForbiddenEntryPoint() {
})
.and()
.authenticationProvider(getProvider())
.formLogin()
.loginProcessingUrl("/api/login")
.successHandler(new AuthentificationLoginSuccessHandler())
.failureHandler(new SimpleUrlAuthenticationFailureHandler())
.and()
.logout()
.logoutUrl("/api/logout")
.logoutSuccessHandler(new AuthentificationLogoutSuccessHandler())
.invalidateHttpSession(true)
.and()
.authorizeRequests()
.anyRequest().authenticated();
}
Others things are the same as in the tutorial.
I'm testing with Postman.
When i add the endpoint i want in CSRF_IGNORE, i can see with logger/debug that token stocked, and token from cookie are the same, because the security config's part CustomCsrfFilter.java in .addFilterAfter() is used, but when i remove the endpoint from this CSRF_IGNORE, what i get is a 403, and, logger/debug in the CustomCsrfFilter.java isn't used, so i'm thinking that tokens aren't compared.
I think I missed something and I would like to understand.
If you want to use CSRF with a http only false cookie, why not use Spring Security's built in CookieCsrfTokenRepository? Should simplify your config that way. CustomCsrfFilter seems to be adding a XSRF-TOKEN cookie to the HttpServletResponse, which CookieCsrfTokenRepository does for you.
The default CSRF cookie name when using CookieCsrfTokenRepository is X-CSRF-TOKEN, which is conveniently the default name Angular's HttpClientXsrfModule uses. Of course you can customize that if you need.
So your security config becomes:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.exceptionHandling()
.authenticationEntryPoint(new Http403ForbiddenEntryPoint())
.and()
.authenticationProvider(getProvider())
.formLogin()
.loginProcessingUrl("/api/login")
.successHandler(new AuthentificationLoginSuccessHandler())
.failureHandler(new SimpleUrlAuthenticationFailureHandler())
.and()
.logout()
.logoutUrl("/api/logout")
.logoutSuccessHandler(new AuthentificationLogoutSuccessHandler())
.invalidateHttpSession(true)
.and()
.authorizeRequests()
.anyRequest().authenticated();
}
And with Angular, your app module has HttpClientXsrfModule as
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule,
HttpClientXsrfModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
I want to implement AuthenticationFailureHandler with the following configuration:
// Auth failure handler
#Bean
public AuthenticationFailureHandler appAuthenticationFailureHandler() {
ExceptionMappingAuthenticationFailureHandler failureHandler = new ExceptionMappingAuthenticationFailureHandler();
Map<String, String> failureUrlMap = new HashMap<>();
failureUrlMap.put(BadCredentialsException.class.getName(), "/login?error");
failureUrlMap.put(AccountExpiredException.class.getName(), "/login?expired");
failureUrlMap.put(LockedException.class.getName(), "/login?locked");
failureUrlMap.put(DisabledException.class.getName(), "/login?disabled");
failureHandler.setExceptionMappings(failureUrlMap);
return failureHandler;
}
and in class SecurityConfiguration extends WebSecurityConfigurerAdapter I have:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/register", "/confirm").permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
// username password
.usernameParameter("username")
.passwordParameter("password")
// success and failure handlers
.successHandler(appAuthenticationSuccessHandler())
.failureHandler(appAuthenticationFailureHandler())
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login?logout")
.invalidateHttpSession(true)
.clearAuthentication(true)
.permitAll()
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler())
;
}
with this, all mentioned above is not redirecting to relevant failure URL, but if I remove
.anyRequest()
.authenticated()
then it is being redirected to relevant failure URL, but that is not good practice now the question is how I can configure the configure() to ignore /login?request parameter and implement further logic accordingly?
As I understand, the issue is that urls like "/login?.*" are available only after authorization. According to spring examples, you can exclude paths from authorized access with the following code in Config file:
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**");
}
When i enable the spring-boot-starter-security dependency. CORS support doesn't work.
This is my SecurityConfiguration Class:
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return authentication -> {
// ...
};
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf()
// Disabling CSRF
.disable()
// Disabling Session Management
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
// Adding custom REST Authentication filter
.addFilterBefore(new RestAuthenticationFilter(authenticationManager()), LogoutFilter.class)
// Authorizing requests
.authorizeRequests()
.antMatchers("/", "/frontend/login")
.permitAll()
.antMatchers("/api/**", "/frontend/**")
.authenticated()
.antMatchers("/**")
.permitAll();
}
}
My Controller Class has a CrossOrigin Annotation:
#CrossOrigin
#RequestMapping("/frontend")
#RestController
public class FrontEndController extends BaseController {
I can handle CORS with custom CORS Filter but I want to use just one Annoation.
I found 2 methods for adding CORS support to spring-security enabled spring-boot project. We can add spring-web CorsFilter to security filter chain. The following example belongs to token based authentication project. So we used a custom RestAuthenticationFilter.
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
source.registerCorsConfiguration("/**", config);
http.csrf()
// Disabling CSRF
.disable()
// Disabling Session Management
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
// Adding spring-web CORS filter
.addFilterBefore(new CorsFilter(source), LogoutFilter.class)
// Adding custom REST Authentication filter
.addFilterBefore(new RestAuthenticationFilter(authenticationManager()), LogoutFilter.class)
// Authorizing requests
.authorizeRequests()
.antMatchers("/", "/frontend/login")
.permitAll()
.antMatchers("/api/**", "/frontend/**")
.authenticated()
.antMatchers("/**")
.permitAll();
}
}
But in the above example our CrossOrigin annotations in the controllers are redundant. So we should give the ability to control CORS requests to spring-web layer. Therefore we can allow CORS pre-flight (OPTIONS HTTP Methods).
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf()
// Disabling CSRF
.disable()
// Disabling Session Management
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
// Adding custom REST Authentication filter
.addFilterBefore(new RestAuthenticationFilter(authenticationManager()), LogoutFilter.class)
// Authorizing requests
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**")
.permitAll()
.antMatchers("/", "/frontend/login")
.permitAll()
.antMatchers("/api/**", "/frontend/**")
.authenticated()
.antMatchers("/**")
.permitAll();
}
}
With the help of above configuration we can use both #CrossOrigin annotations and spring-security configuration.