Files
build-web-application-with-…/9.1.md
2012-10-14 22:48:25 +08:00

5.8 KiB
Raw Blame History

##9.1 预防CSRF攻击

##什么是CSRF CSRFCross-site request forgery中文名称跨站请求伪造也被称为one click attack/session riding缩写为CSRF/XSRF。

那么CSRF到底能够干嘛呢你可以这样简单的理解攻击者可以盗用你的登陆信息以你的身份模拟发送各种请求。攻击者只要借助少许的社会工程诡计例如通过QQ聊天软件发送的链接(有些还伪装成短域名,用户无法分辨)攻击者就能迫使一个Web应用程序的用户去执行攻击者选择的操作。例如如果用户登录网络银行去查看其存款余额他没有退出网络银行系统就点击了一个QQ好友发给他的一个链接地址那么该用户在网络银行帐户中的资金就有可能被转移到攻击者指定的帐户中。

所以当CSRF针对普通用户发动攻击时将对终端用户的数据和操作指令构成严重的威胁当受攻击的终端用户具有管理员帐户的时候CSRF攻击将危及整个Web应用程序。

##CSRF的原理 下图简单阐述了CSRF攻击的思想

从上图可以看出要完成一次CSRF攻击受害者必须依次完成两个步骤

  • 1.登录受信任网站A并在本地生成Cookie 。
  • 2.在不登出A的情况下访问危险网站B。

看到这里读者也许会问“如果我不满足以上两个条件中的一个就不会受到CSRF的攻击”。是的确实如此但你不能保证以下情况不会发生

  • 你不能保证你登录了一个网站后不再打开一个tab页面并访问另外的网站特别现在浏览器都是支持多tab的。
  • 你不能保证你关闭浏览器了后你本地的Cookie立刻过期你上次的会话已经结束。
  • 上图中所谓的攻击网站,可能是一个存在其他漏洞的可信任的经常被人访问的网站。

因此对于用户来说很难避免在登陆一个网站之后点击一些链接进行其他操作所以随时可能成为CSRF的受害者。

CSRF攻击主要是因为Web的隐式身份验证机制Web的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器但却无法保证该请求是用户批准发送的。

##如何预防CSRF 通过上面的介绍读者是否觉得这个攻击很恐怖,意识到恐怖是个好事情,这样会促使你接着往下看如何改进和防止类似的漏洞出现。

我总结了一下看到的资料CSRF的防御可以从服务端和客户端两方面着手防御效果是从服务端着手效果比较好现在一般的CSRF防御也都在服务端进行。

服务端的CSRF方式方法很多样但总的思想都是一致的预防CSRF主要从2个方面入手分别是

  • 1、正确使用GET,POST和Cookie
  • 2、在非GET请求中增加伪随机数

我们上一章介绍过REST方式的Web应用一般而言普通的Web应用都是以GET、POST为主还有一种请求是Cookie方式。我们设计应用的时候一般都是如下方式设计

1、GET常用在查看列举展示的时候

2、POST常用在下达订单改变一个资源的属性或者做其他一些事情

那么我们就指定死我们需要访问的资源的访问方式我就以Go语言举例来说明如何怎么来限制访问的某些方法只能为POST或者GET

mux.Get("/user/:uid", getuser)
mux.Post("/user/:uid", modifyuser)

这样做好之后我们上面图示中GET方式的CSRF就可以防止了因为我们限定了修改只能使用POST当GET方式请求时我们不响应这样就能解决问题了吗当然不是因为POST也是可以模拟的。

那么我们就需要实施第二步在非GET方式的请求中增加随机数这个大概有三种方式来进行

  • 为每个用户生成一个唯一的cookie token所有表单都包含同一个伪随机值这种方案最简单因为攻击者不能获得第三方的Cookie(理论上)所以表单中的数据也就构造失败但是由于用户的Cookie很容易由于网站的XSS漏洞而被盗取所以这个方案必须要在没有XSS的情况下才安全。

  • 每个请求使用验证码,这个方案是完美的,但是在实施过程中就不大可能,易用性上面相当的差。

  • 不同的表单包含一个不同的伪随机值这个我们在4.4小节介绍过如何防止表单多次递交,我们可以复用那边的代码,实现如下:

    生成随机数token

      h := md5.New()
      io.WriteString(h, strconv.FormatInt(crutime, 10))
      io.WriteString(h, "ganraomaxxxxxxxxx")
      token := fmt.Sprintf("%x", h.Sum(nil))
    
      t, _ := template.ParseFiles("login.gtpl")
      t.Execute(w, token)
    

    输出token

      <input type="hidden" name="token" value="{{.}}">
    

    验证token

      r.ParseForm()
      token := r.Form.Get("token")
      if token != "" {
          //验证token的合法性
      } else {
          //不存在token报错
      }	     	
    

这样基本就实现了安全的POST但是也许你会说如果破解了token的算法呢按照理论上是但是实际上破解是基本不可能的因为有人曾计算过暴力破解该串大概需要2的11次方时间。

##总结 跨站请求伪造即CSRF是一种非常危险的Web安全威胁它被Web安全界称为“沉睡的巨人”其威胁程度由此“美誉”便可见一斑。本小节不仅对跨站请求伪造本身进行了简单介绍还详细说明造成这种漏洞的原因所在然后以此提了一些防范该攻击的建议希望对读者编写安全的Web应用能够有所启发。

LastModified

  • Id