安全响应头

1. 前言

上节我们讨论了 Spring Security 如何防范 CSRF 攻击,本节我们讨论如何用最简单的方式提升 Spring Security Web 项目的安全性。

Spring Security 可以通过「HTTP 安全响应头」的方式提升安全性。

本节我们讨论如何实现 HTTP 安全响应头。

2. 安全响应头

Spring Security 提供了一些默认 HTTP 安全性相关的响应头。这些默认响应头如下:

图片描述

2.1 Cache-Control

当用户通过了认证,访问了敏感信息而后点击了退出,此时若浏览器对页面进行了缓存,攻击者就可能通过点击浏览器「后退」按键访问到刚刚认证用户看到的内容。

Spring Security 默认禁用浏览器页面缓存,这样可以减少敏感信息泄漏风险。

2.2 Content Type Options

浏览器为了增强用户体验,会去嗅探用户请求的内容格式。例如当浏览器访问到某个 JavaScript 文件,然而此时并没有指定其文件类型,此时浏览器则会自动判断并且执行它。

内容嗅探带来了一些问题,比如攻击者会利用某些可被解析成多种内容格式的文件,执行 XSS 攻击。例如,有些网站可能允许用户上传和预览 PostScript 代码,攻击者创建一个 PostScript 文档,同时该文档也是一个 JavaScript 格式文件,此时,攻击者便可以实现 XSS 攻击。

Spring Security 默认禁用了内容嗅探功能。

2.3 HTTP Strict Transport Security (HSTS)

通常我们访问网站的时候是直接输入域名,比如:mybank.example.com,此时默认情况下浏览器会访问 http://mybank.example.com。HTTP 协议由于信息不加密,无法保证访问目标的真实性,容易受到中间人攻击。增加 Strict-Transport-Security 响应头,可以强制将访问协议转换成 Https。

2.4 X-Frame-Options

允许网站被放到 iframe 对象里,这种情况下攻击者可能通过设置 CSS,让用户的点击发生在非意愿的位置。

默认情况下 Spring Security 拒绝网页在 iframe 对象中渲染。

2.5 X-XSS-Protection

尽管大部分浏览器都已经内置了过滤 XSS 攻击的支持,但也并非所有浏览器都默认支持。增加 XSS-Protection 头可以确保 XSS 攻击过滤已启动。

3. Spring Security 配置

3.1 默认的安全头配置

Spring Security 提供了一系列默认 Http 安全响应头,我们可以便捷的配置它们。

例如,为 X-Frame-Options 指定值 SAMEORIGIN

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) {
        http.headers(headers -> headers.frameOptions(frameOptions -> frameOptions.sameOrigin()));
    }
}

如果不希望默认头被自动添加,可以通过如下方式配置,例如仅添加 Cache Control 头:

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // ...
            .headers(headers -> headers
                // 取消自动添加默认头
                .defaultsDisabled()
                // 添加 CacheControl 头
                .cacheControl(withDefaults())
            );
    }
}

或者通过以下方式禁用所有 HTTP 安全响应头:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.headers(headers -> headers.disable());
    }
}

3.2 Cache Control

Spring Security 默认包含「Cache Control」头。

如果只需要缓存指定类型的内容,我们可以通过 HttpServletResponse.setHeader(String,String) 方式指定缓存项,如 CSS、JavaScript、图片等。

我们也可以直接禁用掉「Cache Control」头,方式如下:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) {
        http.headers(headers -> headers.cacheControl(cache -> cache.disable()));
    }
}

3.3 Content-Type 头

Spring Security 默认包含了 Content-Type 头。如果需要禁用它,可以通过如下方式:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) {
        http.headers(headers -> headers.contentTypeOptions(contentTypeOptions -> contentTypeOptions.disable()));
    }
}

3.4 HSTS 头

HTTP Strict Transport Security(HSTS)头,在 Spring Security 中是被默认开启的,我们可以修改它的默认状态,如下:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .headers(headers -> headers
                .httpStrictTransportSecurity(hsts -> hsts
                    .includeSubDomains(true)
                    .preload(true)
                    .maxAgeInSeconds(31536000)
                )
            );
    }
}

3.5 X-Frame-Options 头

默认情况下,Spring Security 通过 X-Frame-Options 头方式禁止 iframe 层渲染,我们可以通过如下方式修改其配置:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.headers(headers -> headers.frameOptions(frameOptions -> frameOptions.sameOrigin()));
    }
}

3.6 X-XSS-Protection 头

默认情况下,Spring Security 为了防止 XSS 攻击增加了 X-XSS-Protection 头,我们可以通过以下方式对其配置进行修改:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.headers(headers -> headers.xssProtection(xss -> xss.block(false)));
    }
}

3.7 自定义响应头

Spring Security 的实现机制使得它可以轻易的添加安全响应头。

3.7.1 静态头

例如配置如下自定义头:

X-Custom-Security-Header: header-value

我们可以通过 StaticHeadersWriter 对象添加:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.headers(headers -> headers.addHeaderWriter(new StaticHeadersWriter("X-Custom-Security-Header","header-value")));
    }
}

3.7.2 动态添加

当Spring Security 的默认配置方式不能满足我们添加响应头的需求时,我们可以通过 HeadersWriter 实例动态添加。例如动态添加 X-Frame-Options 头:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.headers(headers -> headers.addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN)));
    }
}

3.7.3 为指定请求添加响应头

有时候我们只想为某个确定的请求添加响应头,例如只针对对登录页面的请求,这是可以通过 DelegatingRequestMatcherHeaderWriter 实现。例如:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        RequestMatcher matcher = new AntPathRequestMatcher("/login");
        DelegatingRequestMatcherHeaderWriter headerWriter = new DelegatingRequestMatcherHeaderWriter(matcher,new XFrameOptionsHeaderWriter());
        http.headers(headers -> headers.frameOptions(frameOptions -> frameOptions.disable()).addHeaderWriter(headerWriter));
    }
}

4. 小结

本节讨论了如何利用 HTTP 请求头提升 Spring Security Web 项目的安全性,主要内容有:

图片描述

  • Http 安全响应头是提升 B/S 应用安全性的有效手段;
  • Spring Security 默认实现了常用的安全响应头;
  • Spring Security 可以通过配置类,直接开启、关闭或调整各个内置请求头的状态及参数;
  • Spring Security 提供了静态设置响应头的方法;
  • Spring Security 支持动态添加响应头;
  • Spring Security 支持针对某个特殊请求动态设置响应头。

下节我们讨论针对 HTTP 协议,我们还能有哪些安全性提升策略。