Thursday, June 16, 2016

Disabling Spring Security headers does not work

Leave a Comment

I need to disable the cache control headers in my Spring Security conf.

According to the documentation a simple http.headers.disable() should do it, but I still see the

Cache-Control:no-cache, no-store, max-age=0, must-revalidate Expires:0 Pragma:no-cache 

headers in responses.

My current security config is:

http.antMatcher("/myPath/**") // "myPath" is of course not the real path     .headers().disable()     .authorizeRequests()      // ... abbreviated     .anyRequest().authenticated(); 

Things I've tried so far:

application.properties

I added the security.headers.cache=false line, but that made no difference.

Using a filter

I tried the following filter:

@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {   chain.doFilter(request, new HttpServletResponseWrapper((HttpServletResponse) response) {       @Override       public void setHeader(String name, String value) {         if (name.equalsIgnoreCase("Cache-Control")) {           value = "";         } else if (name.equalsIgnoreCase("Expires")) {           value = "";         } else if (name.equalsIgnoreCase("Pragma")) {           value = "";         }         super.setHeader(name, value);       }   }); } 

After adding logging I saw that this filter only writes the X-XSS-Protection header, all the cache headers are written somewhere later and this filter doesn't have access to "override" them. This happens even if I add this filter at the last position of the security filter chain.

Using an interceptor

I tried the following interceptor:

@Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {     String requestUri = request.getRequestURI();     response.setHeader("Cache-Control", "max-age=3600");     response.setHeader("Expires", "3600");     response.setHeader("Pragma", ""); } 

This (quite predictably) just added the headers, meaning that the original no-cache headers still appear in addition to the ones added by the interceptor.

I'm at my wits end here. How do I get rid of the cache control header set by Spring security?

4 Answers

Answers 1

You’ll need a class that extends WebSecurityConfigurerAdapter with two overidden configure methods to configure the filter and the authentication provider. For example, the following works at a bare minimum:

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;  @Configuration @EnableWebMvcSecurity @EnableGlobalMethodSecurity(securedEnabled = true) public class SecurityConfigDemo extends WebSecurityConfigurerAdapter {      @Autowired     private DemoAuthenticationProvider demoAuthenticationProvider;      @Override     protected void configure(HttpSecurity http) throws Exception {              // Prevent the HTTP response header of "Pragma: no-cache".     http.headers().cacheControl().disable();      }      @Override     public void configure(AuthenticationManagerBuilder auth) throws Exception {                 auth.authenticationProvider(demoAuthenticationProvider);             }      } 

You can also disabe Spring Security completely for public static resources as following (in the same class as above):

@Override public void configure(WebSecurity web) throws Exception {     web.ignoring().antMatchers("/static/public/**"); } 

This requires configuring two resource handlers to get cache control headers right:

@Configuration public class MvcConfigurer extends WebMvcConfigurerAdapter         implements EmbeddedServletContainerCustomizer {     @Override     public void addResourceHandlers(ResourceHandlerRegistry registry) {         // Resources without Spring Security. No cache control response headers.         registry.addResourceHandler("/static/public/**")             .addResourceLocations("classpath:/static/public/");          // Resources controlled by Spring Security, which         // adds "Cache-Control: must-revalidate".         registry.addResourceHandler("/static/**")             .addResourceLocations("classpath:/static/")             .setCachePeriod(3600*24);     } } 

Answers 2

It may be help :

@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter {  @Override protected void configure(HttpSecurity http) throws Exception {     http     // ...     .headers()         .defaultsDisabled()         .cacheControl(); } } 

http://docs.spring.io/spring-security/site/docs/current/reference/html/headers.html#headers-cache-control

Answers 3

So, I found the answer myself: I finally made the Cache-Control header to change its value by creating a new entry in my yml configuration file called spring.resources.cachePeriod and set it to a value different than 0. The bad thing is that all resources use this setting, so no way to make it different depending on the resource, as far as I know.

The answer to this question helped a lot.

Answers 4

You are right that using

http     .headers().disable()     ... 

will disable your headers. If you only want cache control disabled, you can use the following:

http     .headers()         .cacheControl().disable()         .and()     ... 

I have posted a sample that demonstrates this working along with a test.

My guess is the problem you are having is that you have multiple HttpSecurity configurations. Remember that if you have:

http     .antMatchers("/myPath/**")     ... 

Only URLs that start with /myPath/ will be impacted. Additionally, if you have multiple HttpSecurity instances each HttpSecurity instance is considered in order and only the first HttpSecurity instance is used. For example if you have:

@Configuration @Order(1) public class MyPathAdminWebSecurityConfig extends WebSecurityConfigurerAdapter {      @Override     protected void configure(HttpSecurity http) throws Exception {         http             .antMatchers("/myPath/admin/**")             .authorizeRequests()                 .anyRequest().hasRole("ADMIN");     } }  @Configuration @Order(2) public class MyPathWebSecurityConfig extends WebSecurityConfigurerAdapter {      @Override     protected void configure(HttpSecurity http) throws Exception {         http             .antMatchers("/myPath/**")             .headers()                 .cacheControl().disable();     } }  @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter {      @Override     protected void configure(HttpSecurity http) throws Exception {         http             .authorizeRequests()                 .anyRequest().authenticated();     } } 

If you request /myPath/admin/abc

First MyPathAdminWebSecurityConfig is considered. Since /myPath/admin/ starts with /myPath/admin/ we will use MyPathAdminWebSecurityConfig and not consider any other configuration. This means you will expect to get headers back for this request.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment