欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

同步令牌模式防范CSRF跨站请求伪造攻击

程序员文章站 2022-03-08 18:43:21
...

什么是“跨渣请求伪造”呢?这是信息安全领域的一个名词,译自英文“Cross Site Request Forgery”。

百度百科上介绍的很简单却很明了,大家可以看一下,我这里配合一些代码稍微多说一点。

 

假设我们要在银行网站上给老妈转100块钱,毕竟毕业这么多年了也没给过家里钱(虽然你认为他们都在赚钱不需要你给,况且你自己现在赚钱刚好可以经济独立,不过实际上爹妈还是很希望你能支援家里的)。这个转账的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

 你现在已经登录银行的网站了(你说你用的客户端?那当然没有这个危险性了,所以银行才都推荐U盾的),然后你新开了个标签页,进入一个“危机四伏”的网站(你傻啊,为什么要这样做。其实当别人没骗的时候我们都可以马后炮,但是没有足够的防范意识,任何时候我们被骗都是在鼓里)。

这个网站有一个链接,代码类似于这样:

<form action="https://bank.example.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="点我拿红包!"/>
</form>

 除了一个“拿红包”的按钮之外,其他都是隐藏域。一看抢红包了,赶紧抢吧!这个表单提交后,和上面的请求是完全一样的。尽管这个网站没有你的cookie信息,可是你提交的时候cookie照样会被发送,所以它完全不需要拿到你的cookie。

你说:好像只要我足够小心,不去点击按钮就行了。当然不是,因为给你个按钮让你点是貌似愚蠢的做法,这个网站打开的同时,它的javascript代码说不定就不停的发生那个请求了。而你完全不知道。

 

为什么会这样?

因为接收action的服务器并不知道请求是跨站的,跨不跨站对于服务器来说没什么两样。

知道了这一点,我们的解决方法也就应运而生了:增加点东西让其他站点提供不了(因为只用cookie是不够的),这样服务器去验证这个域不就可以了吗。

一种方案是同步令牌模式。你的每一次请求,除了session数据外,都会提供服务器经response返回的随机串作为一个单独的http参数。提交后服务器会验证这个随机串。而其他网站是拿不到这个随机串的。

与之前的请求相比,只多了一个参数_csrf:

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>

对于经过浏览器进行访问和发送接收请求的场景,防范CSRF攻击都有必要。 

 

 Spring Secure是怎么防范CSRF攻击呢?

首先,我们使用spring secure应该保证action的HTTP动词是合适的,不应该使用GET请求处理编辑性活动。使用POST要好得多。有的框架检测到token(就是上面提到的随机串)不合法马上就将用户登出要求重新登录。而Spring是产生一个403状态。

spring默认是开启 csrf防范的(如果使用命名空间需要开发者显示开启),如果想关闭,可以使用disable()方法:

@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends
   WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable();
  }
}

 最后,要保证在除了GET外的请求中都包含_csrf域。不然,连登出都会失败(咦,登出不是GET吗?所以最好用POST)。