上文总结了csrf攻击以及一些常用的防护方式,csrf全称Cross-site request forgery(跨站请求伪造),是一类利用信任用户已经获取的注册凭证,绕过后台用户验证,向被攻击网站发送未被用户授权的跨站请求以对被攻击网站执行某项操作的一种恶意攻击方式。

  上面的定义比较抽象,我们先来举一个简单的例子来详细解释一下csrf攻击,帮助理解。

  假设你通过电脑登录银行网站进行转账,一般这类转账页面其实是一个form表单,点击转账其实就是提交表单,向后台发起http请求,请求的格式大概像下面这个样子:

复制代码
POST /transfer HTTP/1.1 Host: xxx.bank.com Cookie: JSESSIONID=randomid; Domain=xxx.bank.com; Secure; HttpOnly Content-Type: application/x-www-form-urlencoded  amount=100.00&routingNumber=1234&account=8888
复制代码

  好了,现在给自己的账户转完账了,但是这时你一般不会立马退出银行网站的登录,你可能会紧接着去上网浏览别的网页,碰巧你上网的时候看到一些很吸引人眼球的广告(比如在家兼职轻松月入上万。。。之类的),你点击了一下,但是发现什么也没有,也许你会关掉这个网页,以为什么都没有发生。但是后台可能已经发生了一系列的事情,如果这是个钓鱼网站,并且刚才你点击的页面恰好又包含一个form表单,如下所示:

复制代码
<form action="https://xxx.bank.com/transfer" method="post">  <input type="hidden"       name="amount"       value="100.00"/>  <input type="hidden"       name="routingNumber"       value="evilsRoutingNumber"/>  <input type="hidden"       name="account"       value="evilsAccountNumber"/>  <input type="submit"       value="Win Money!"/></form>
复制代码

  这里只要你点击网页便会自动提交表单,导致你向一个陌生账户转账100元(这些都可通过js实现自动化),而且是未经过你的授权的情况下,这就是csrf的攻击方式,虽然其不知道你的登录信息,但是其利用浏览器自身的机制来冒充用户绕过后台用户验证从而发起攻击。

  csrf是一种常见的web攻击方式,一些现有的安全框架中都对该攻击的防护提供了支持,比如spring security,从4.0开始,默认就会启用CSRF保护,会针对PATCH,POST,PUT和DELETE方法进行防护。本文会结合spring security提供的防护方法,并结合其源码来学习一下其内部防护原理,本文涉及到的Spring Security源码版本为5.1.5。

  本文目录如下:

  使用Spring Security防护CSRF攻击

  Spring Security的CSRF防护原理

  总结

 

1. 使用Spring Security防护CSRF攻击

  通过Spring Security来防护CSRF攻击需要做哪些配置呢,总结如下:

  • 使用合适的HTTP请求方式
  • 配置CSRF保护
  • 使用CSRF Token

1.1 使用合适的HTTP请求方式

  第一步是确保要保护的网站暴露的接口使用合适的HTTP请求方式,就是在还未开启Security的CSRF之前需要确保所有的接口都只支持使用PATCH、POST、PUT、DELETE这四种请求方式之一来修改后端数据。

  这并不是Spring Security在防护CSRF攻击方面的自身限制,而是合理防护CSRF攻击所必须做的,原因是通过GET的方式传递私有数据容易导致其泄露,使用POST来传递敏感数据更合理。

1.2 配置CSRF保护

  下一步就是将Spring Security引入你的后台应用中。有些框架通过让用户session失效来处理无效的CSRF Token,但是这种方式是有问题的,取而代之,Spring Security默认返回一个403的HTTP状态码来拒绝无效访问,可以通过配置AccessDeniedHandler来实现自己的拒绝逻辑。

  如果项目中是采用的XML配置,则必须显示的使用<csrf>标签元素来开启CSRF防护,详见<csrf>

  通过Java配置的方式则会默认开启CSRF防护,如果希望禁用这一功能,则需要手动配置,见下面的示例,更详细的配置可以参考csrf()方法的官方文档。