18. Cross Site Request Forgery (CSRF)(跨站点请求伪造(CSRF))

本节讨论Spring Security的跨站点请求伪造(CSRF)支持。

18.1 CSRF Attacks(CSRF攻击)

在我们讨论Spring Security如何保护应用程序免受CSRF攻击之前,我们将解释什么是CSRF攻击。让我们看一个具体的例子来更好地理解。假设您的银行网站提供了一个表单,允许将资金从当前登录的用户转移到另一个银行帐户。例如,HTTP请求可能如下所示:


 

现在假装你认证到你的银行网站,然后,不注销,访问一个邪恶的网站。邪恶网站包含一个具有以下形式的网页:


 

你喜欢赢钱,所以你点击提交按钮。在此过程中,您无意中向恶意用户转移了100美元。发生这种情况是因为,虽然邪恶的网站看不到您的cookie,但与您的银行相关的cookie仍然会随请求一起发送。

最糟糕的是,这整个过程可能已经使用JavaScript自动化。这意味着你甚至没有需要点击的按钮。那么我们该如何保护自己免受此类攻击?

18.2 Synchronizer Token Pattern(同步令牌模式)

问题是来自银行网站的HTTP请求和来自邪恶网站的请求完全一样。这意味着无法拒绝来自邪恶网站的请求,也无法允许来自银行网站的请求。为了防范CSRF攻击,我们需要确保请求中有邪恶网站无法提供的东西。

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

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

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


 

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

18.3 When to use CSRF protection(何时使用CSRF保护)

你应该什么时候使用CSRF保护?我们的建议是对任何可以由普通用户通过浏览器处理的请求使用CSRF保护。如果您只创建由非浏览器客户端使用的服务,您可能会希望禁用CSRF保护。

 18.3.1 CSRF protection and JSON(CSRF保护和JSON)

一个常见的问题是“我需要保护javascript发出的JSON请求吗?”简而言之,这要看情况。但是,您必须非常小心,因为CSRF漏洞会影响JSON请求。例如,恶意用户可以使用以下形式创建一个带有JSON的CSRF:


 

这将产生下面的JSON结构


 

如果应用程序没有验证内容类型,那么它将暴露于此漏洞。根据设置的不同,验证内容类型的Spring MVC应用程序仍然可以通过将URL后缀更新为以”结尾来利用。json "如下所示:


 

18.3.2 CSRF and Stateless Browser Applications(CSRF和无状态浏览器应用)

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

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

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

18.4 Using Spring Security CSRF Protection

那么,使用Spring Security’s保护我们的网站免受CSRF攻击的必要步骤是什么呢?使用弹簧安全CSRF保护的步骤概述如下:

1、使用适当的HTTP动词

2、配置CSRF保护

3、包括CSRF令牌

18.4.1 Use proper HTTP verbs

防范CSRF攻击的第一步是确保你的网站使用正确的HTTP动词。具体来说,在Spring Security的CSRF支持可以使用之前,您需要确定您的应用程序正在使用PATCH、POST、PUT和/或DELETE来修改状态。

这不是对Spring Security的支持的限制,而是对适当的CSRF预防的一般要求。原因是在HTTP GET中包含私有信息会导致信息泄露。参见URI的RFC 2616第15.1.3节“对敏感信息进行编码”,了解对敏感信息使用开机自检(POST)代替“获取”(GET)的一般指导。

18.4.2 Configure CSRF Protection

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

从Spring Security 4.0开始,默认情况下使用XML配置启用CSRF保护。如果您想禁用CSRF保护,下面可以看到相应的XML配置。


 

默认情况下,通过Java配置启用CSRF保护。如果您想禁用CSRF,下面可以看到相应的Java配置。有关如何配置CSRF保护的其他自定义信息,请参考csrf()的Javadoc。


 18.4.3 Include the CSRF Token

Form Submissions

最后一步是确保您将CSRF令牌包含在所有的补丁、开机自检、放入和删除方法中。解决这个问题的一种方法是使用_csrf请求属性来获取当前的CsrfToken。下面是一个用JSP实现这一点的例子:


 

一种更简单的方法是使用Spring Security JSP标签库中的csrfInput标签。

如果您正在使用Spring MVC <form:form >标记或Thymeleaf2.1+并且正在使用@EnableWebSecurity,则会自动为您包含CsrfToken(使用CsrfRequestDataValueProcessor)。

Ajax and JSON Requests

如果您使用的是JSON,那么就不可能在一个HTTP参数中提交CSRF令牌。相反,您可以在一个HTTP头中提交令牌。典型的模式是在元标签中包含CSRF令牌。一个带有JSP的示例如下所示:


 

除了手动创建元标签,您还可以使用Spring Security JSP标签库中更简单的csrfMetaTags标签。

然后,您可以在所有Ajax请求中包含该令牌。如果您使用的是jQuery,这可以通过以下方式实现:


 

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


 

已配置的客户端可以与需要向CSRF受保护资源发出请求的应用程序的任何组件共享。rest.js和jQuery的一个显著区别是,只有用配置好的客户端发出的请求才会包含CSRF令牌,而jQuery中所有请求都会包含令牌。确定哪些请求接收令牌的能力有助于防止CSRF令牌泄露给第三方。有关rest.js的更多信息,请参考rest.js参考文档。

CookieCsrfTokenRepository

在某些情况下,用户会希望将CsrfToken保存在cookie中。默认情况下,CookieCsrfTokenRepository将写入名为XSRF-TOKEN的cookie,并从名为X-XSRF-TOKEN的头或HTTP参数_csrf中读取它。这些默认值来自AngularJS

您可以使用以下方法在XML中配置CookieCsrfTokenRepository:


 

该示例显式设置cookieHttpOnly=false。这是允许JavaScript(即AngularJS)读取它所必需的。如果您不需要用JavaScript直接读取cookie的能力,建议省略cookieHttpOnly=false以提高安全性。

您可以使用以下方法在Java配置中配置CookieCsrfTokenRepository:


 

该示例显式设置cookieHttpOnly=false。这是允许JavaScript(即AngularJS)读取它所必需的。如果您不需要使用JavaScript直接读取cookie的能力,建议省略cookieHttpOnly=false(通过使用新的CookieCsrfTokenRepository()来提高安全性)。

18.5 CSRF Caveats(CSRF 事项)

实施CSRF时有几个注意事项。

18.5.1 Timeouts

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

有人可能会问,为什么默认情况下预期的CsrfToken不存储在cookie中。这是因为存在已知的漏洞,其中报头(即指定cookies)可以由另一个域设置。

这也是为什么Ruby on Rails不再跳过CSRF检查的原因。有关如何利用漏洞的详细信息,请参见这个webappsec.org线程。另一个缺点是,通过删除状态(即超时),如果令牌被破坏,您将失去强制终止令牌的能力。

减轻活动用户超时的一个简单方法是使用一些JavaScript,让用户知道他们的会话即将到期。用户可以单击按钮继续并刷新会话。或者,指定自定义的AccessDeniedHandler 允许您以任何方式处理InvalidCsrfTokenException。有关如何自定义AccessDeniedHandler的示例,请参考提供的针对xml和Java配置的链接。

最后,可以将应用程序配置为使用不会过期的CookieCsrfTokenRepository。如前所述,这并不像使用会话那样安全,但在许多情况下已经足够好了。

18.5.2 Logging In

为了防止伪造登录请求,登录表单也应该防止CSRF攻击。由于CsrfToken存储在HttpSession中,这意味着一旦访问了CsrfToken令牌属性,就会创建一个HttpSession。虽然这在RESTful /无状态架构中听起来很糟糕,但现实是状态对于实现实际安全性是必要的。没有状态,如果一个令牌被破坏了,我们将无能为力。实际上,CSRF令牌的尺寸非常小,对我们的架构影响应该可以忽略不计。

保护表单中的登录的一种常见技术是在表单提交之前使用JavaScript函数获取有效的CSRF令牌。通过这样做,就不需要考虑会话超时(在上一节中讨论过),因为会话是在表单提交之前创建的(假设没有配置CookieCsrfTokenRepository),所以用户可以留在登录页面上,在需要时提交用户名/密码。为了实现这一点,您可以利用Spring Security提供的CsrfTokenArgumentResolver,并公开一个端点,如这里所述。

18.5.3 Logging Out

添加CSRF将更新登录过滤器,使其仅使用超文本传输协议。这可以确保注销需要CSRF令牌,并且恶意用户不能强行注销您的用户。

一种方法是使用注销表单。如果你真的想要一个链接,你可以使用JavaScript让这个链接执行一个开机自检(例如,可能在一个隐藏的表单上)。对于禁用了JavaScript的浏览器,您可以选择让链接将用户带到将执行开机自检的注销确认页面。

如果您真的想在注销时使用HTTP GET,您可以这样做,但是请记住,这通常是不推荐的。例如,以下Java配置将使用网址执行注销/使用任何HTTP方法请求注销:


 18.5.4 Multipart (file upload)

对多部分/表单数据使用CSRF保护有两种选择。每个选项都有其利弊。

1、将多部分过滤器置于Spring Security之前

2、将CSRF令牌包括在行动中

在您将Spring Security的CSRF保护与多部分文件上传集成之前,请确保您可以在没有CSRF保护的情况下进行上传。关于在Spring中使用多部分表单的更多信息可以在Spring参考的17.10 Spring的多部分(文件上传)支持部分和多部分过滤器javadoc中找到。

Placing MultipartFilter before Spring Security

第一个选项是确保在Spring Security过滤器之前指定MultipartFilter 。在Spring安全过滤器之前指定MultipartFilter 意味着没有调用MultipartFilter 的授权,这意味着任何人都可以在您的服务器上放置临时文件。但是,只有授权用户才能提交由您的应用程序处理的文件。一般来说,这是推荐的方法,因为临时文件上传对大多数服务器的影响可以忽略不计。

为了确保在使用java配置的Spring Security filter之前指定MultipartFilter ,用户可以覆盖beforeSpringSecurityFilterChain,如下所示:


 

为了确保在使用XML配置的Spring Security过滤器之前指定MultipartFilter ,用户可以确保将MultipartFilter 的<filter-mapping>元素放在web.xml中的springSecurityFilterChain之前,如下所示:


 

Include CSRF token in action

如果允许未授权用户上传临时文件是不可接受的,另一种方法是将MultipartFilter 放在Spring Security过滤器之后,并将CSRF作为查询参数包含在表单的action属性中。下面是一个jsp示例


 

这种方法的缺点是查询参数可能会被泄露。一般来说,最好将敏感数据放在正文或标题中,以确保不被泄露。更多信息可在中找到RFC 2616 Section 15.1.3 Encoding Sensitive Information in URI’s.

18.5.5 HiddenHttpMethodFilter

 HiddenHttpMethodFilter应该放在Spring安全过滤器之前。总的来说,这是真的,但它可能有额外的含义时,保护免受CSRF袭击。请注意,HiddenHttpMethodFilter只在开机自检时覆盖HTTP方法,因此这实际上不太可能导致任何真正的问题。但是,最好的做法仍然是确保它放在Spring Security的过滤器之前。

 18.6 Overriding Defaults

Spring Security的目标是提供保护用户免受攻击的默认值。这并不意味着你被迫接受它的所有默认值。

例如,您可以提供自定义的CsrfTokenRepository来覆盖CsrfToken的存储方式。

您还可以指定一个自定义的请求匹配器来确定哪些请求受CSRF保护(例如,您可能不在乎是否利用了注销)。简而言之,如果Spring Security的CSRF保护没有按照您希望的那样运行,您可以自定义该行为。有关如何使用XML进行这些自定义的详细信息,请参阅Section 41.1.18, “<csrf>” 文档,有关如何在使用Java配置时进行这些自定义的详细信息,请参阅CsrfConfigurer javadoc文档。

原文地址:https://www.cnblogs.com/jrkl/p/13513332.html