Spring Boot 2 Set-Cookie response header missing - java

please forgive any lack of structure/etiquette, this is my first post.
I have a Java application using Spring Boot (v2.1.6) with annotation based configuration for spring-boot-starter-security and cannot understand how to add the response header of:
"Set-Cookie: SameSite=strict"
to resolve the warning:
A cookie associated with a cross-site resource at "myApiUrl" was set without the `SameSite` attribute. A future release of Chrome will only deliver cookies with cross-site requests if they are set with `SameSite=None` and `Secure`. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/feature/5088147346030592 and https://www.chromestatus.com/feature/5633521622188032.
The full configuration for my HttpSecurity is listed below and I have attempted to add the header in the last portion of the configuration using StaticHeadersWriter as such:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.cors()
.and()
.exceptionHandling()
.authenticationEntryPoint(entryPoint) //request is generally sent to entry point when unauthorized
.and()
.authorizeRequests()
.antMatchers("/loggedIn").permitAll()
.antMatchers("/createUser").permitAll()
.antMatchers("/deleteUser").hasRole("ADMIN")
.antMatchers("/fullDB").hasRole("ADMIN")
.antMatchers("/logs").hasRole("ADMIN")
.antMatchers("/**").authenticated()
.and()
.formLogin()
.successHandler(new AuthenticationSuccessHandler())
.failureHandler(new SimpleUrlAuthenticationFailureHandler())
.and()
.logout()
.deleteCookies(cookieName)
.invalidateHttpSession(true)
.logoutUrl("/logout")
.logoutSuccessHandler((new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK)))
.and()
.headers()
.addHeaderWriter(new StaticHeadersWriter("Set-Cookie", "SameSite=strict"))
.addHeaderWriter(new StaticHeadersWriter("A-cookie","B=val"))
.addHeaderWriter(new StaticHeadersWriter("SetCookie","SameSitestrict"));
}
Using the following configuration I am able to receive a valid response with every header except the Set-Cookie header, which appears to get removed and can be seen in the below link.
Image of Response
Additional attempts at resolving this issue that I have tried included adding a redirect in the provided successHandler() to a custom url endpoint, which would then return a ResponseEntity where I could provide the code below, however these attempts also would not have the "Set-Cookie" response header included.
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HttpHeaders.SET_COOKIE, "SomeName=someId");
return new ResponseEntity(httpHeaders, HttpStatus.OK);
or
response.addCookie(new Cookie("SomeName", "someId"));
return ResponseEntity.ok().build();
Any advice on how to provide the "Set-Cookie" header on the response from a Spring Boot API call to resolve this chrome warning would be greatly appreciated!

Related

Configure Spring Security to accept login details from POST body instead of request params

I am building a rest application using Spring boot and Spring security. In the login module I want my code to read username and password, sent from a client, from Post body. But my code reads only from the url, like Get request.
I tried to make a request from the Postman. Set the method as Post. When i set the username and password in the request param field. I get the "200 ok", but when I send the parameters as json in Post request, I get 401 unauthorized. What configuration changes do I need to make?
my Spring security config:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/registration").permitAll()
.antMatchers("/admin/**").hasAuthority("Admin")
.antMatchers("/db").hasAuthority("DBA")
.antMatchers("/user").hasAuthority("USER").anyRequest()
.authenticated().and().csrf().disable().formLogin()
.loginPage("/login")
.failureHandler(customFailureHandler)
.successHandler(customSuccessHandler)
.usernameParameter("username")
.passwordParameter("password")
.and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/").and().exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedPage("/access-denied");
}

Permit few requests without authentication, and authenticate remaining all request

I have configured my resource server with below http configuration. Basically I want to set configuration like permit few requests without authentication and all other request which I have not specified should be allowed only if the user has valid access token.
Note that I am using spring security oauth2 and spring boot.
#Override
public void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.and()
.csrf()
.requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
.disable()
.headers()
.frameOptions().disable().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.antMatcher("**")
.authorizeRequests()
.antMatchers("/hello/")
.permitAll()
.anyRequest()
.authenticated();
}
When I try to access http://localhost:8889/hello, It gives me below 401 UnAuthorized response even though I have configured it with permitAll
{
"error": "unauthorized",
"error_description": "An Authentication object was not found in the SecurityContext"
}
On the contrary I checked with below configuration
.authorizeRequests()
.antMatchers("/hello/").permitAll()
.antMatchers("/hello2/").permitAll()
.antMatchers("/secure/**").authenticated()
.antMatchers("/secure2/**").authenticated();
It worked well like hello an hello2 are allowed without token and, secure and secure2 gives error response if token is not passed or invalid token is passed.
Can someone please tell me what I am missing?

Spring Security Access Denied Handler gives HTTP Status 405 - Request method 'POST' not supported

On updating Spring version from 4.2.2 to 4.3.5, I am facing a problem while logging in from login.jsp page.
When I delete all cookies and then try to login it gives HTTP 403 Access Denied due to missing CSRF token that should be handled by the accessDeniedHandler but the when the accessDeniedHandler forwards the request to "forbidden.jsp" page, it gives an error of "HTTP Status 405 - Request method 'POST' not supported".
Here is the code of configuration for WebSecurityConfigurerAdapter
protected void configure(HttpSecurity http) throws Exception {
SimpleUrlLogoutSuccessHandler logoutSuccessHandler = new SimpleUrlLogoutSuccessHandler();
logoutSuccessHandler.setDefaultTargetUrl("/login?logout");
logoutSuccessHandler.setTargetUrlParameter("targetUrl");
// #formatter:off
http
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutSuccessHandler(logoutSuccessHandler)
.permitAll()
.and()
.exceptionHandling()
.accessDeniedPage("/forbidden")
.and()
.rememberMe()
.rememberMeServices(appRememberMeServices)
.key(appRememberMeServices.getKey());
// #formatter:on
authorizeRequests(http);
http.headers().frameOptions().sameOrigin();
}
With spring version 4.2.2 it works fine with no code changes.
When I add a controller and allow "POST" request on "/forbidden", it works fine but when I add a view controller mapping it expects a "GET" request but gets a "POST" request.
So if anyone knows what is the change in spring version 4.3.5 from 4.2.2 that it is making a "POST" request rather than a "GET" request to access denied page.

X-CSRF-TOKEN is not generated by Spring Boot

I followed the guide here: http://spring.io/guides/gs/rest-service/ to build my rest service example and now I am trying to enable the CSRF protection. I read that it should be enabled by default, so if I DON'T include:
http.csrf().disable()
in my WebSecurityConfigurerAdapter configuration, the CSRF protectection should be enabled by default, but it does not seem to to be the case. The problem is that the X-CSRF-TOKEN is not generated and not included in my HTTP response in any way.
What am I expected to do, to have the x-csrf-token generated and included in the response and, of course, the csrf protection fully working?
I noticed that, with a similar spring mvc configuration, I get the x-csrf-token generated simply including:
< security:csrf disabled="false"/>
in my security configuration file. But, with spring boot maybe I am getting something wrong and there is no way to have the csrf token generated. Can anybody help me, perhaps pointing me to a working example? My security configuration is:
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
// .csrf().disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic()
.authenticationEntryPoint(new RestAuthenticationEntryPoint())
.and()
.formLogin()
.successHandler(new RestAuthenticationSuccessHandler())
.failureHandler(new SimpleUrlAuthenticationFailureHandler())
.and()
.logout()
.logoutSuccessHandler(new RestLogoutSuccessHandler());
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(restUserDetailService);
}
To include the CSRF Token in your csrf protection, you can include CSRFTokenRepository to generate tokens. To illustrate in your case adding a simple line is enough:
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) //HERE ! Defaults XSRF-TOKEN as cookie name and X-XSRF-TOKEN as header name
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic()
.authenticationEntryPoint(new RestAuthenticationEntryPoint())
.and()
.formLogin()
.successHandler(new RestAuthenticationSuccessHandler())
.failureHandler(new SimpleUrlAuthenticationFailureHandler())
.and()
.logout()
.logoutSuccessHandler(new RestLogoutSuccessHandler());}
Using Spring security 5.3.0.Final, one of the ways you can generate the CSRF token is by setting it in the cookie using the following code below.
http.csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
You also need to include the generated CSRF token in your request for the server to authorize.
<form>
<input type="hidden" name="_csrf" value="${cookie['XSRF-TOKEN'].getValue()}" />
//Code goes here
</form>
In the event you're using a JS framework, you need to include the token by setting it in the request header.
Here is an example for a JQuery ajax call.
// Get the CSRF token from the cookie
const csrfCookie= document.cookie.replace(/(?:(?:^|.*;\s*)XSRF-TOKEN\s*\=\s*([^;]*).*$)|^.*$/, '$1');
// Add the CSRF token to each ajax request header
settings.beforeSend = function(xhr) {
xhr.setRequestHeader('X-XSRF-TOKEN', springCsrfCookie);
};
$.ajax(settings);
There are other implementations that will suit your needs documented in the following link by Spring | https://docs.spring.io/spring-security/site/docs/5.3.0.RELEASE/reference/html5/#servlet-csrf
We had pretty similar issue during our security tests where we suspected that we accidentally disable csfr in configure method of websecurityconfig class,by default it is enabled. by changing the congfigure method as shown below , we had spring automatically generate csfr tokens.
websecurityconfig class configure method==>
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/login","/loginError","/home","/interruption").permitAll()
.antMatchers("/admin").hasAuthority(Roles.ROLE_PREFIX.role()+Roles.HALLEYYNT01.role())
.antMatchers("/requests").hasAuthority(Roles.ROLE_PREFIX.role()+Roles.CCHALLEYLOGIN.role())
.antMatchers("/solrequests").hasAuthority(Roles.ROLE_PREFIX.role()+Roles.SOLHALLEYLOGIN.role())
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
//.failureUrl("/loginError")
.loginProcessingUrl("/authenticate")
.defaultSuccessUrl("/")
.and()
.logout().clearAuthentication(true).invalidateHttpSession(true).deleteCookies("JSESSIONID")
.logoutSuccessUrl("/login");
//.and()
//.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
}

Disable BasicAuth on specific sub paths

I know there are some question on stackoverflow, but nothing helped...
http
.addFilterBefore(RestConfiguration.getCorsFilter(), ChannelProcessingFilter.class)
.authorizeRequests() //Authorize Request Configuration
.antMatchers("/api/**").hasRole("API")
.antMatchers("/api/confirm/**").permitAll()
.antMatchers("/api/version").permitAll()
.anyRequest().authenticated()
.and() //HTTP basic Authentication only for API
.antMatcher("/api/**").httpBasic()
.and() // angularjs requires csrf
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
.csrf().disable();
I've a api path with some sub paths. But I want to access two of them without basic auth (confirm/** and version).
How can I do that? I always get the login dialog.
You should use antMatchers("/api/**").hasRole("API") rule after /api/confirm/** and /api/version. Current ordering is not quite right:
.antMatchers("/api/confirm/**").permitAll()
.antMatchers("/api/version").permitAll()
.antMatchers("/api/**").hasRole("API")
To sum up, if you want to only secure /api/* and let public access on /api/confirm/**, /api/version and all other paths without api prefix, you should have a HttpSecurity config like this:
http
// Same as before
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/api/confirm/**").permitAll()
.antMatchers("/api/version").permitAll()
.antMatchers("/api/**").hasRole("API")
.anyRequest().permitAll();
// Same as before

Categories

Resources