vlambda博客
学习文章列表

读书笔记《spring-security-third-edition》额外的 Spring 安全特性

额外的 Spring 安全特性

在本章中,我们将探索本书迄今为止尚未涵盖的其他几个 Spring Security 特性,包括以下主题:

  • Cross-Site Scripting (XSS)
  • Cross-Site Request Forgery (CSRF)
  • Synchronizer tokens
  • Clickjacking

我们将了解如何使用以下方法包含各种 HTTP 标头以防止常见的安全漏洞:

  • Cache-Control
  • Content-Type Options
  • HTTP Strict Transport Security (HSTS)
  • X-Frame-Options
  • X-XSS-Protection

在阅读本章之前,您应该已经了解 Spring Security 的工作原理。这意味着您应该已经能够在一个简单的 Web 应用程序中设置身份验证和授权。如果你无法做到这一点,你需要确保你已经阅读了 章节3自定义身份验证,在继续本章之前。如果您牢记 Spring Security 的基本概念并且了解您要集成的框架,那么与其他框架的集成相当简单。

安全漏洞

在互联网时代,有许多可能的漏洞可以被利用。 The Open Web Application Security Project (OWASP) 是一个很好的资源来了解更多关于基于 Web 的漏洞,它位于 https://www.owasp.org

除了作为了解各种漏洞的重要资源之外,OWASP 还根据行业趋势对前 10 名漏洞进行了分类。

跨站脚本

XSS 攻击涉及已注入受信任站点的恶意脚本。

XSS 攻击发生在攻击者利用给定的 Web 应用程序时,该 Web 应用程序允许不通风的输入通常以基于浏览器的脚本的形式发送到站点,然后由网站的不同用户执行。

根据提供给网站的经过验证或未编码的信息,攻击者可以利用多种形式。

此问题的核心是期望用户信任正在发送的站点信息。最终用户的浏览器无法知道该脚本不应被信任,因为他们正在浏览的网站存在隐含信任。因为它认为脚本来自受信任的来源,所以恶意脚本可以访问浏览器保留并与该网站一起使用的任何 cookie、会话令牌或其他敏感信息。

XSS 可以用下面的时序图来描述:

读书笔记《spring-security-third-edition》额外的 Spring 安全特性

跨站请求伪造

CSRF 是一种欺骗受害者提交恶意请求的攻击。这种类型的攻击继承或劫持受害者的​​身份和特权,并代表受害者执行未经授权的功能和访问。

对于 Web 应用程序,大多数浏览器会自动包含与站点关联的凭据,其中包括用户会话、cookie、IP 地址、Windows 域凭据等。

因此,如果用户当前在某个站点上进行了身份验证,那么该给定站点将无法区分受害者发送的伪造请求和合法的法庭请求。

CSRF 攻击的目标是导致服务器状态更改的功能,例如更改受害者的电子邮件地址或密码,或参与金融交易。

这迫使受害者检索对攻击者没有好处的数据,因为攻击者没有收到响应;受害者会。因此,CSRF 攻击针对的是状态改变请求。

以下序列图详细说明了 CSRF 攻击是如何发生的:

读书笔记《spring-security-third-edition》额外的 Spring 安全特性

可以采取几种不同的设计措施来尝试阻止 CSRF,但是,诸如秘密 cookie、HTTP POST 请求、多步事务、URL 重写和 HTTPS 等措施,并不能阻止这种类型的攻击。

OWASP 的 10 大安全漏洞列表详细说明了 CSRF 作为第八大最常见的攻击 https://www.owasp.org/index.php/Cross-Site_Request_Forgery_( CSRF)

同步器令牌

对此的解决方案是使用同步器令牌模式。这个解决方案是为了确保每个请求除了我们的会话 cookie 之外,还需要一个随机生成的令牌作为 HTTP 参数。提交请求时,服务器必须查找参数的预期值并将其与请求中的实际值进行比较。如果值不匹配,则请求应该失败。

Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet 推荐同步器令牌模式作为 CSRF 攻击的可行解决方案: https://www.owasp.org/index.php /Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#General_Recommendation:_Synchronizer_Token_Pattern

放宽期望是只需要每个更新状态的 HTTP 请求的令牌。这可以安全地完成,因为同源策略确保恶意站点无法读取响应。此外,我们不希望在 HTTP GET 中包含随机令牌,因为这会导致令牌泄露。

让我们看看我们的示例将如何变化。假设随机生成的令牌存在于一个名为 named_csrf 的 HTTP 参数中。例如,转账请求如下所示:

POST /transfer HTTP/1.1
Host: bank.example.com
Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly
Content-Type: application/x-www-form-urlencoded
amount=100.00&routingNumber=1234&account=9876&_csrf=<secure-random token>

您会注意到我们添加了带有随机值的 _csrf 参数。现在,恶意网站将无法猜测 _csrf 参数的正确值(必须在恶意网站上明确提供),并且当服务器将实际令牌与实际令牌进行比较时,传输将失败预期的令牌。

下图显示了同步器令牌模式的标准用例:

读书笔记《spring-security-third-edition》额外的 Spring 安全特性

Spring Security 中的同步器令牌支持

Spring Security 提供了默认开启的同步器令牌支持。您可能已经从前面的章节中注意到,在我们的 SecurityConfig.java 文件中,我们禁用了 CSRF 保护,如下面的代码片段所示:

//src/main/java/com/packtpub/springsecurity/configuration/SecurityConfig.java

protected void configure(HttpSecurity http) throws Exception {
...
// CSRF protection is enabled by default.
http.csrf().disable();
...
}

到目前为止,我们已经禁用了同步器令牌保护,因此我们可以专注于其他安全问题。

如果我们此时启动应用程序,我们可以通过安全性运行,并且不会向任何页面添加同步器令牌支持。

您应该从以下代码开始 第 16.00 章-日历

何时使用 CSRF 保护

建议您对任何可以由浏览器或普通用户处理的请求使用 CSRF 保护。如果您只创建非浏览器客户端使用的服务,您很可能希望禁用 CSRF 保护。

CSRF 保护和 JSON

一个常见的问题是:我需要保护 JavaScript 发出的 JSON 请求吗?简短的回答是,这取决于。但是,您必须非常小心,因为存在可能影响 JSON 请求的 CSRF 漏洞。例如,恶意用户可以使用以下形式创建带有 JSON 的 CSRF:

    <form action="https://example.com/secureTransaction" method="post" enctype="text/plain">
    <input name='{"amount":100,"routingNumber":"maliciousRoutingNumber", "account":"evilsAccountNumber", "ignore_me":"' value='test"}' type='hidden'>
    <input type="submit" value="Win Money!"/>
    </form>This will produce the following JSON structure{ "amount":   
    100,"routingNumber": "maliciousRoutingNumber","account": 
    "maliciousAccountNumber","ignore_me": "=test"
    }

如果一个应用程序没有验证 Content-Type 方法,那么它就会暴露在这个漏洞中。根据设置,通过将 URL 后缀更新为以 .json 结尾,仍然可以利用验证 Content-Type 方法的 Spring MVC 应用程序,如以下代码所示:

    <form action="https://example.com/secureTransaction.json" method="post" enctype="text/plain">
    <input name='{"amount":100,"routingNumber":"maliciousRoutingNumber", "account":"maliciousAccountNumber", "ignore_me":"' value='test"}' type='hidden'>
    <input type="submit" value="Win Money!"/>
    </form>

CSRF 和无状态浏览器应用程序

如果您的应用程序是无状态的怎么办?这并不一定意味着您受到保护。事实上,如果用户不需要在 Web 浏览器中针对给定请求执行任何操作,他们很可能仍然容易受到 CSRF 攻击。

例如,考虑一个使用自定义 cookie 的应用程序,该 cookie 包含其中用于身份验证的所有状态,而不是 JSESSIONID cookie。当 CSRF 攻击发生时,自定义 cookie 将与请求一起发送,方式与前面示例中发送 JSESSIONID cookie 的方式相同。

使用基本身份验证的用户也容易受到 CSRF 攻击,因为浏览器会自动在任何请求中包含用户名和密码,就像在我们之前的示例中发送 JSESSIONID cookie 一样。

使用 Spring Security CSRF 保护

那么,使用 Spring Security 保护我们的站点免受 CSRF 攻击的必要步骤是什么?使用 Spring Security 的 CSRF 保护的步骤如下:

  1. Use proper HTTP verbs.
  2. Configure CSRF protection.
  3. Include the CSRF token.

使用正确的 HTTP 动词

防止 CSRF 攻击的第一步是确保您的网站使用正确的 HTTP 动词。具体来说,在使用 Spring Security 的 CSRF 支持之前,您需要确定您的应用程序正在使用 PATCHPOSTPUT 和/ 或 DELETE 用于修改状态的任何内容。

这不是 Spring Security 支持的限制,而是正确预防 CSRF 的一般要求。原因是在 HTTP GET 方法中包含私有信息会导致信息泄露。

请参阅 RFC 2616第 15.1.3 节在 URI 中编码敏感信息,了解使用 POST 的一般指导kbd> 而不是 GET 用于敏感信息 (https://www.w3.org/Protocols/rfc2616/rfc2616-sec15.html#sec15.1.3)。

配置 CSRF 保护

下一步是在您的应用程序中包含 Spring Security 的 CSRF 保护。一些框架通过使用户的会话无效来处理无效的 CSRF 令牌,但这会导致其自身的问题。相反,默认情况下,Spring Security 的 CSRF 保护将产生 HTTP 403 访问被拒绝。这可以通过配置 AccessDeniedHandler 以不同方式处理 InvalidCsrfTokenException 来定制。

出于被动原因,如果您使用 XML 配置,则必须使用 <csrf> 元素显式启用 CSRF 保护。有关其他自定义,请参阅 <csrf> 元素的文档。

记录 SEC-2347 以确保 Spring Security 4.x 的 XML 命名空间配置默认启用 CSRF 保护 (https://github.com/spring-projects/spring-security/issues/2574)。

默认 CSRF 支持

CSRF 保护在 Java 配置中默认启用。请参阅 csrf() 的 Javadoc,了解有关如何配置 CSRF 保护的其他自定义。

只是为了在这个配置中详细一点,我们将把 CSRS 方法添加到我们的 SecurityConfig.java 文件中,如下所示:

//src/main/java/com/packtpub/springsecurity/configuration/SecurityConfig.java
    @Override
    public void configure(HttpSecurity http) throws Exception {
       http.csrf();
    }

提交中包含 CSRF 令牌

最后一步是确保在所有 PATCHPOSTPUTDELETE 中包含 CSRF 令牌方法。解决此问题的一种方法是使用 _csrf 请求属性来获取当前的 CsrfToken 令牌。使用 JSP 执行此操作的示例如下所示:

    <c:url var="logoutUrl" value="/logout"/>
    <form action="${logoutUrl}" method="post">
      <input type="submit" value="Log out" />
      <input type="hidden"name="${_csrf.parameterName}" value="${_csrf.token}"/>
    </form>

使用 Spring Security JSP 标签库包含 CSRF 令牌

如果启用了 CSRF 保护,此标签会插入一个隐藏的表单字段,其中包含 CSRF 保护令牌的正确名称和值。如果没有启用 CSRF 保护,则此标签没有输出。

通常,Spring Security 会自动为您使用的任何 标签插入 CSRF 表单字段,但如果由于某种原因您不能使用 , csrfInput 是一个方便的替代品。

您应该将此标记放在 HTML <form></form> 块中,您通常会在其中放置其他输入字段。不要将此标签放置在 Spring 块中。 Spring Security 自动处理 Spring 表单,如下所示:

    <form method="post" action="/logout">
      <sec:csrfInput />
      ...
    </form>

默认 CSRF 令牌支持

如果您使用 Spring MVC <form:form> 标签或 Thymeleaf 2.1+,并将 @EnableWebSecurity 替换为 @EnableWebMvcSecurityCsrfToken 令牌会自动为您包含(使用我们一直在处理的 CsrfRequestDataValue 令牌)。

所以,对于这本书,我们所有的网页都使用了 Thymeleaf。如果我们在 Spring Security 中启用 CSRF 支持,Thymeleaf 默认启用 CSRF 支持。

您应该从以下代码开始 第 16.01 章-日历

如果我们启动 JBCP 日历应用程序并导航到登录页面 https://localhost:8443/login.html,我们可以查看生成的 login.html页面,如下:

    <form method="POST" action="/login" ...>
      ...
      <input type="hidden" name="_csrf" value="e86c9744-5b7d-4d5f-81d5-450463222908">
    </form>

Ajax 和 JSON 请求

如果您使用的是 JSON,则无法在 HTTP 参数中提交 CSRF 令牌。相反,您可以在 HTTP 标头中提交令牌。一个典型的模式是在 <meta> HTML 标记中包含 CSRF 令牌。使用 JSP 的示例如下:

    <html>
       <head>
         <meta name="_csrf" content="${_csrf.token}"/>
         <!-- default header name is X-CSRF-TOKEN -->
         <meta name="_csrf_header" content="${_csrf.headerName}"/>
         ...
       </head>

您可以使用 Spring Security JSP 标记库中更简单的 csrfMetaTags 标记,而不是手动创建元标记。

csrfMetaTags 标签

如果启用了 CSRF 保护,则此标签插入包含 CSRF 保护令牌表单字段、标头名称和 CSRF 保护令牌值的元标签。这些元标记对于在应用程序的 JavaScript 中使用 CSRF 保护很有用。

您应该将 csrfMetaTags 标记放置在 HTML <head></head> 块中,您通常会在其中放置其他元标记。使用此标签后,您可以使用 JavaScript 轻松访问表单字段名称、标题名称和令牌值,如下所示:

<html>
   <head>
       ...
       <sec:csrfMetaTags />
       <script type="text/javascript" language="javascript">
           var csrfParameter = $("meta[name='_csrf_parameter']").attr("content");
           var csrfHeader = $("meta[name='_csrf_header']").attr("content");
           var csrfToken = $("meta[name='_csrf']").attr("content");
           ...
       <script>
   </head>
   ...

如果没有启用 CSRF 保护,csrfMetaTags 什么也不输出。

jQuery 用法

然后,您可以在所有 Ajax 请求中包含该令牌。如果您使用的是 jQuery,则可以使用以下代码片段来完成:

$(function () {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(e, xhr, options) {
   xhr.setRequestHeader(header, token);
});
});

使用 cujoJS 的 rest.js 模块

作为 jQuery 的替代方案,我们建议使用 cujoJS 的 rest.js 模块。 rest.js 模块为以 RESTful 方式处理 HTTP 请求和响应提供了高级支持。核心能力是对 HTTP 客户端进行上下文化的能力,通过将拦截器链接到客户端来根据需要添加行为,如下所示:

    var client = rest.chain(csrf, {
    token: $("meta[name='_csrf']").attr("content"),
    name: $("meta[name='_csrf_header']").attr("content")
    });

配置的客户端可以与需要向 CSRF 受保护资源发出请求的应用程序的任何组件共享。 rest.js 和 jQuery 之间的一个显着区别是,使用配置的客户端发出的唯一请求将包含 CSRF 令牌,而在 jQuery 中,所有请求都将包含令牌。确定哪些请求接收令牌的能力有助于防止将 CSRF 令牌泄露给第三方。

请参考 rest.js 参考文档以获取更多信息 rest.js
( https://github.com/cujojs/rest/tree/master/docs )。

CSRF 警告

在 Spring Security 中实现 CSRF 时需要注意一些注意事项。

超时

一个问题是预期的 CSRF 令牌存储在 HttpSession 方法中,因此一旦 HttpSession 方法到期,您配置的 AccessDeniedHandler 处理程序将接收 InvalidCsrfTokenException。如果您使用默认的 AccessDeniedHandler 处理程序,浏览器将收到 HTTP 403 并显示错误消息。

您可能会问为什么预期的 CsrfToken 令牌没有存储在 cookie 中。这是因为存在已知的漏洞,其中标头(指定 cookie)可以由另一个域设置。

这与 Ruby on Rails 在 X-Requested-With 标头存在时不再跳过 CSRF 检查的原因相同 (http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails/< /a>)。

Web 应用程序安全联盟http://www.webappsec.org ) 有一个关于使用 CSRF 和 HTTP 307 重定向的详细线程来执行 利用 CSRF cookie。

另一个缺点是,通过删除状态(超时),如果某些内容受到破坏,您将失去强制终止令牌的能力。

缓解活跃用户超时的一种简单方法是使用一些 JavaScript 让用户知道他们的会话即将到期。用户可以单击按钮继续并刷新会话。

或者,指定自定义 AccessDeniedHandler 处理程序允许您以任何您喜欢的方式处理 InvalidCsrfTokenException,正如我们在以下代码中看到的那样:

//src/main/java/com/packtpub/springsecurity/configuration/SecurityConfig.java

@Override
public void configure(HttpSecurity http) throws Exception {
   http.exceptionHandling()
           .accessDeniedHandler(accessDeniedHandler);
}
@Bean
public CustomAccessDeniedHandler accessDeniedHandler(){
   return new CustomAccessDeniedHandler();
}

在登录

为了防止伪造登录请求,登录表单也应该受到保护以免受 CSRF 攻击。由于 CsrfToken 令牌存储在 HttpSession 中,这意味着 HttpSession 方法将在 CsrfToken 立即创建属性被访问。虽然这在 RESTful/无状态架构中听起来很糟糕,但现实情况是状态是实现实际安全所必需的。如果没有状态,如果令牌被泄露,我们将无能为力。实际上,CSRF 令牌的大小非常小,对我们的架构的影响应该可以忽略不计。

攻击者可能伪造请求让受害者登录 使用攻击者凭据的目标网站;这被称为登录 CSRF (https://en.wikipedia.org/wiki/Cross- site_request_forgery#Forging_login_requests)。

注销

添加 CSRF 将更新 LogoutFilter 过滤器以仅使用 HTTP POST。这确保了注销需要 CSRF 令牌,并且恶意用户无法强制注销您的用户。

一种方法是使用 <form> 标记进行注销。如果需要 HTML 链接,可以使用 JavaScript 让链接执行 HTTP POST(可以是隐藏形式)。对于禁用 JavaScript 的浏览器,您可以选择让该链接将用户带到将执行 HTTP POST 的注销确认页面。

如果您想在注销时使用 HTTP GET,您可以这样做,但请记住,通常不建议这样做。例如,以下 Java 配置将在使用任何 HTTP 方法请求注销 URL 模式时执行注销

//src/main/java/com/packtpub/springsecurity/configuration/SecurityConfig.java

@Override
protected void configure(HttpSecurity http) throws Exception {
   http.logout()
       .logoutRequestMatcher(
           new AntPathRequestMatcher("/logout"));
}

安全 HTTP 响应标头

以下部分讨论 Spring Security 对向响应添加各种安全标头的支持。

默认安全标头

Spring Security 允许用户轻松注入默认安全标头以帮助保护他们的应用程序。以下是 Spring Security 提供的当前默认安全头列表:

  • Cache-Control
  • Content-Type Options
  • HTTP Strict Transport Security
  • X-Frame-Options
  • X-XSS-Protection

虽然这些标头中的每一个都被认为是最佳实践,但应注意并非所有客户端都使用这些标头,因此鼓励进行额外测试。出于被动原因,如果您使用 Spring Security 的 XML 命名空间支持,则必须显式启用安全标头。使用 <headers> 元素可以轻松添加所有默认标题,而无需子元素。

SEC-2348 被记录以确保 Spring Security 4.x 的 XML 命名空间配置将默认启用安全标头(https://github.com/spring-projects/spring-security/issues/2575)。

如果您使用 Spring Security 的 Java 配置,则默认添加所有默认安全标头。可以使用 Java 配置禁用它们,如下所示:

//src/main/java/com/packtpub/springsecurity/configuration/SecurityConfig.java

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

以下代码将安全标头添加到响应中。这在使用 WebSecurityConfigurerAdapter 的默认构造函数时默认激活。接受 WebSecurityConfigurerAdapter 提供的默认值,或仅调用 headers() 方法而不调用其他方法,等效于以下代码片段:

@Override
protected void configure(HttpSecurity http) throws Exception {
   http
     .headers()
         .contentTypeOptions()
         .and()
         .xssProtection()
         .and()
         .cacheControl()
         .and()
         .httpStrictTransportSecurity()
         .and()
         .frameOptions()
         .and()
     ...;
}

一旦您指定了应包含的任何标头,则只会包含这些标头。例如,以下配置将仅包括对 X-Frame-Options 的支持:

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

缓存控制

过去,Spring Security 要求您为 Web 应用程序提供自己的 Cache-Control 方法。这在当时似乎是合理的,但浏览器缓存已经发展到包括用于安全连接的缓存。这意味着用户可以查看经过身份验证的页面,然后注销,然后恶意用户可以使用浏览器历史记录查看缓存的页面。

为了帮助缓解这种情况,Spring Security 添加了 Cache-Control 支持,它将在您的响应中插入以下标头:

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

简单地添加没有子元素的 headers() 方法将自动添加 Cache-Control 和相当多的其他保护选项。但是,如果您只想要 Cache-Control,则可以使用 Spring Security 的 Java 配置和 cacheControl() 方法启用此功能,如下所示:

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

如果要缓存特定响应,您的应用程序可以选择性地调用 HttpServletResponse.setHeader(String,String) 来覆盖 Spring Security 设置的标头。这对于确保正确缓存 CSS、JavaScript 和图像等内容很有用。

使用 Spring Web MVC 时,这通常在您的配置中完成。例如,以下配置将确保为所有资源设置缓存标头:

@EnableWebMvc
public class WebMvcConfiguration
extends WebMvcConfigurerAdapter {
   @Override
   public void addResourceHandlers(
                   ResourceHandlerRegistry registry) {
      registry
           .addResourceHandler("/resources/**")
           .addResourceLocations("/resources/")
           .setCachePeriod(3_155_6926);
   }
   // ...
}

内容类型选项

从历史上看,包括 Internet Explorer 在内的浏览器会尝试使用内容嗅探来猜测请求的内容类型。这允许浏览器通过猜测未指定内容类型的资源的内容类型来改善用户体验。例如,如果浏览器遇到没有指定内容类型的 JavaScript 文件,它将能够猜测内容类型然后执行它。

在允许上传内容时,还需要做许多额外的事情,例如仅在不同的域中显示文档、确保设置 Content-Type 标头、清理文档等等。但是,这些措施超出了 Spring Security 提供的范围。同样重要的是要指出,在禁用内容嗅探时,您必须指定内容类型才能正常工作。

内容嗅探的问题在于,这允许恶意用户使用多语言(可作为多种内容类型有效的文件)来执行 XSS 攻击。例如,某些网站可能允许用户向网站提交有效的 postscript 文档并进行查看。恶意用户可能会创建一个也是有效 JavaScript 文件的 postscript 文档并使用它执行 XSS 攻击 (http://webblaze.cs.berkeley.edu/papers/barth-caballero-song.pdf)。

可以通过在我们的响应中添加以下标头来禁用内容嗅探:

    X-Content-Type-Options: nosniff

Cache-Control 元素一样,当使用没有子元素的 headers() 方法时,默认添加 nosniff 指令。 X-Content-Type-Options 标头默认添加在 Spring Security Java 配置中。如果您想对标头进行更多控制,可以使用以下代码显式指定内容类型选项:

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

HTTP 严格传输安全

当您输入银行网站时,您输入的是 mybank.example.com,还是输入 https://mybank.example.com?如果省略 HTTPS 协议,则可能容易受到中间人攻击。即使网站执行重定向到 https://mybank.example.com,恶意用户也可以拦截初始 HTTP 请求并操纵响应(重定向到 < kbd>https://mibank.example.com 并窃取他们的凭据)。

许多用户忽略了 HTTPS 协议,这就是创建 HSTS 的原因。

根据 RFC6797,HSTS 标头仅注入到 HTTPS 响应中。为了让浏览器确认标头,浏览器必须首先信任签署用于建立连接的 SSL 证书的 CA,而不仅仅是 SSL 证书 (https://tools.ietf.org/html/rfc6797)。

一旦将 mybank.example.com 添加为 HSTS 主机,浏览器就可以预先知道对 mybank.example.com 的任何请求都应该被解释为 https: //mybank.example.com。这大大降低了中间人攻击发生的可能性。

将站点标记为 HSTS 主机的一种方法是将主机预加载到浏览器中。另一种是将 Strict-Transport-Security 标头添加到响应中。例如,以下命令将指示浏览器将域视为 HSTS 主机一年(一年中大约有 31,536,000 秒):

    Strict-Transport-Security: max-age=31536000 ; includeSubDomains

可选的 includeSubDomains 指令指示 Spring Security 子域(例如 secure.mybank.example.com)也应该被视为 HSTS 域。

与其他标头一样,当 headers() 方法未指定子元素时,Spring Security 会将前一个标头添加到响应中,但在您使用 Java 配置时会自动添加。您也可以仅将 HSTS 标头与 hsts() 方法一起使用,如以下代码所示:

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

X 框架选项

允许将您的网站添加到框架中可能是一个安全问题。例如,使用巧妙的 CSS 样式,用户可能会被诱骗点击他们不打算点击的内容。

https://www.youtube.com/watch?v=3mk0RySeNsU< 查看点击劫持视频演示/一>。

例如,登录到其银行的用户可能会单击授予其他用户访问权限的按钮。这种攻击被称为点击劫持。

https://www.owasp.org/index.php/Clickjacking

另一种处理点击劫持的现代方法是使用内容安全策略。 Spring Security 不提供对此的支持,因为规范尚未发布,而且它有点复杂。但是,您可以使用静态标题功能来实现这一点。要及时了解此问题并了解如何使用 Spring Security 实现它,请参阅 https://github.com/spring-projects/spring-security/issues/2342

有多种方法可以减轻点击劫持攻击。例如,为了保护旧版浏览器免受 Clickjacking 攻击,您可以使用断帧代码。虽然不完美,但对于旧版浏览器来说,破帧代码是最好的。

解决 Clickjacking 的一种更现代的方法是使用 X-Frame-Options 标头,如下所示:

    X-Frame-Options: DENY

X-Frame-Options 响应标头指示浏览器阻止响应中包含此标头的任何站点在框架内呈现。与其他响应头一样,当 headers() 方法指定时没有子元素时,它会自动包含在内。您还可以显式指定 frame-options 元素来控制将哪些标头添加到响应中,如下所示:

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

如果要更改 X-Frame-Options 标头的值,则可以使用 XFrameOptionsHeaderWriter 实例

一些浏览器内置了过滤反射 XSS 攻击的支持。这绝不是万无一失的,但它确实有助于 XSS 保护。

过滤通常默认启用,因此添加标头只是确保启用它并指示浏览器在检测到 XSS 攻击时执行什么操作。例如,过滤器可能会尝试以侵入性最小的方式更改内容以仍然呈现所有内容。有时,这种类型的替换本身可能成为 XSS 漏洞。相反,最好阻止内容,而不是尝试修复它。为此,我们可以添加以下标头:

    X-XSS-Protection: 1; mode=block

headers() 方法没有指定子元素时,默认情况下会包含此标头。我们可以使用 xssProtection 元素显式声明它,如下所示:

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

自定义标题

Spring Security 有一些机制可以方便地向您的应用程序添加更多常见的安全标头。但是,它还提供了挂钩以启用添加自定义标头。

静态标题

有时您可能希望将自定义安全标头注入到您的应用程序中,这些标头不支持开箱即用。例如,也许您希望尽早支持内容安全策略,以确保仅从同一来源加载资源。由于对内容安全策略的支持尚未最终确定,浏览器使用两个常见的扩展标头之一来实现该功能。这意味着我们需要两次注入策略。可以在以下代码片段中看到标头的示例:

X-Content-Security-Policy: default-src 'self'
X-WebKit-CSP: default-src 'self'

使用 Java 配置时,可以使用 header() 方法将这些标头添加到响应中,如下所示:

@Override
protected void configure(HttpSecurity http) throws Exception {
   http.headers()
       .addHeaderWriter(
         new StaticHeadersWriter(
               "X-Content-Security-Policy",
               "default-src 'self'"))
       .addHeaderWriter(
           new StaticHeadersWriter(
               "X-WebKit-CSP",
               "default-src 'self'"));
}

HeadersWriter 实例

当命名空间或 Java 配置不支持您想要的标头时,您可以创建自定义 HeadersWriter 实例,甚至提供 HeadersWriter 的自定义实现。

我们来看一个使用XFrameOptionsHeaderWriter的自定义实例的例子。也许您希望允许为同一来源构建内容。这很容易通过将策略属性设置为 SAMEORIGIN 来支持,但让我们看一个使用 ref 属性的更明确的示例,如以下代码片段所示:

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

DelegatingRequestMatcherHeaderWriter 类

有时,您可能只想为某些请求编写标头。例如,也许您只想保护您的登录页面不被陷害。您可以使用 DelegatingRequestMatcherHeaderWriter 类来执行此操作。使用 Java 配置时,可以使用以下代码来完成:

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

概括

在本章中,我们介绍了几个安全漏洞,并使用 Spring Security 来规避这些漏洞。阅读本章后,您应该了解 CSRF 的威胁以及使用同步器令牌来防止 CSRF。

您还应该知道如何使用 Cache-ControlContent-Type Options、HSTS、X-Frame- 包含各种 HTTP 标头以防止常见的安全漏洞选项X-XSS-Protection 方法。

在下一章中,我们将讨论如何从 Spring Security 3.x 迁移到 Spring Security 4.2。