菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
0
0

GoWeb教程_09.1. 预防CSRF攻击

原创
05/13 14:22
阅读数 329

什么是 CSRF

CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

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

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

CSRF 的原理

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

图9.1 CSRF 的攻击过程

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

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

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

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

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

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

如何预防 CSRF

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

CSRF 的防御可以从服务端和客户端两方面着手,防御效果是从服务端着手效果比较好,现在一般的 CSRF 防御也都在服务端进行。

服务端的预防 CSRF 攻击的方式方法有多种,但思想上都是差不多的,主要从以下2个方面入手:

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

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

  • 1.GET 常用在查看,列举,展示等不需要改变资源属性的时候;
  • 2.POST 常用在下达订单,改变一个资源的属性或者做其他一些事情;

接下来我就以 Go 语言来举例说明,如何限制对资源的访问方法:

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

这样处理后,因为我们限定了修改只能使用 POST,当 GET 方式请求时就拒绝响应,所以上面图示中 GET 方式的 CSRF 攻击就可以防止了,但这样就能全部解决问题了吗?当然不是,因为 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 应用能够有所启发。

发表评论

0/200
0 点赞
0 评论
收藏
为你推荐 换一批