web 安全中经常谈到的两个东西: XSS 和 CSRF.
这两个概念在前端面试中也经常被问到, 只要涉及到 WEB 安全的东西就必须提到他们哥俩, 从我以往的面试经历中我多次死在这两个东西上, 知道这两个东西但让我仔细描述下就瞎几把乱扯, 结果可想而知. 这几天也查阅了相关书籍, 终于把这两个东西搞明白了, 为此决定写篇文章来记录他们俩以备今后复习, 这篇先介绍 CSRF.
CSRF 是什么鬼
CSRF(Cross Site Request Forgery) 跨站请求伪造. 也被称为 One Click Attack 和 Session Riding, 通常缩写为 CSRF 或 XSRF. 如果从名字你还不不知道它表示什么, 你可以这样理解: 攻击者 (黑客, 钓鱼网站) 盗用了你的身份, 以你的名义发送恶意请求, 这些请求包括发送邮件, 发送消息, 盗取账号, 购买商品, 银行转账, 从而使你的个人隐私泄露和财产损失.
CSRF 漏洞现状
CSRF 这种攻击方式在 2000 年已经被国外的安全人员提出, 但在国内, 直到 2006 年才开始被关注, 2008 年, 国内外的多个大型社区和交互网站分别爆出 CSRF 漏洞, 如: 纽约时报, Metafilter,YouTube 和百度.... 而现在, 互联网的许多站点仍对此毫无防备, 以至于安全业界称 CSRF 为 "沉睡的巨人".
CSRF 攻击实例
听了这么多, 可能大家还云里雾里, 光听概念可能大家对于 CSRF 还是不够了解, 下面我将举一个例子来让大家对 CSRF 有一个更深层次的理解.
我们先假设支付宝存在 CSRF 漏洞, 我的支付宝账号是 lyq, 攻击者的支付宝账号是 xxx. 然后我们通过网页请求的方式 http://zhifubao.com/withdraw?account=lyq&amount=10000&for=lyq2 可以把我账号 lyq 的 10000 元转到我的另外一个账号 lyq2 上去. 通常情况下, 该请求发送到支付宝服务器后, 服务器会先验证该请求是否来自一个合法的 session 并且该 session 的用户已经成功登陆. 攻击者在支付吧也有账号 xxx, 他知道上文中的 URL 可以进行转账操作, 于是他自己可以发送一个请求 http://zhifubao.com/withdraw?account=lyq&amount=10000&for=xxx 到支付宝后台. 但是这个请求是来自攻击者而不是来自我 lyq, 所以不能通过安全认证, 因此该请求作废. 这时, 攻击者 xxx 想到了用 CSRF 的方式, 他自己做了个黄色网站, 在网站中放了如下代码: http://zhifubao.com/withdraw?account=lyq&amount=10000&for=xxx 并且通过黄色链接诱使我来访问他的网站. 当我禁不住诱惑时就会点了进去, 上述请求就会从我自己的浏览器发送到支付宝, 而且这个请求会附带我的浏览器中的 cookie. 大多数情况下, 该请求会失败, 因为支付宝要求我的认证信息, 但是我如果刚访问支付宝不久, 还没有关闭支付宝页面, 我的浏览器中的 cookie 存有我的认证信息, 这个请求就会得到响应
, 从我的账户中转 10000 元到 xxx 账户里, 而我丝毫不知情, 攻击者拿到钱后逍遥法外. 所以以后一定要克制住自己, 不要随便打开别人的链接.
CSRF 原理
下面一张图简单阐述了 CSRF 的原理
csrf 原理
从上图可以看出, 要完成一次 CSRF 攻击, 受害者必须依次完成以下两个步骤:
登录受信任网站 A, 并在本地生成 Cookie.
在不登出 A 的情况下, 访问危险网站 B.
看到这里, 你也许会问:"如果我不满足以上两个条件中的一个, 我就不会受到 CSRF 攻击". 是滴, 确实如此, 但是你不能保证以下情况不会发生:
你不能保证你登录了一个网站之后, 不再打开一个 tab 页面并访问其它的网站(黄网).
你不能保证你关闭浏览器之后, 你本地的 Cookie 立刻过期, 你上次的会话已经结束.
上述中所谓的攻击网站, 可能就是一个钓鱼网站或者黄色网站.
CSRF 如何防御
验证 HTTP Referer 字段
根据 HTTP 协议, 在 HTTP 头部中有一个 Referer 字段, 它记录了该 HTTP 请求所在的地址, 表示 HTTP 请求从那个页面发出的. 比如当你访问 http://zhifubao.com/withdraw?account=lyq&amount=10000&for=xxx , 用户必须先登录支付宝网站, 然后通过点击页面的的按钮来触发转账事件. 此时, 转账请求的 Referer 值就是转账页面所在的 URL, 通常是以 zhifubao.com 域名开头的地址. 如果攻击者要实行 CSRF 攻击, 那么他只能在自己的站点构造请求, 此时 Referer 的值就指向黑客自己的网站. 因此要防御 CSRF 攻击, 支付宝只需要对每一个转账请求验证其 Referer 值, 如果是以 zhifubao.com 开头的域名, 则是合法请求, 相反, 则是非法请求并拒绝.
这种方法的好处就是简单易行, 只需要在后台添加一个拦截器来检查 Referer 即可. 然而这种办法并不是万无一失, Referer 的值是由浏览器提供的, 一些低级的浏览器可以通过某种方式篡改 Referer 的值, 这就给了攻击者可乘之机; 而一些高级浏览器处于安全考虑, 可以让用户设置发送 HTTP 请求时不再提供 Referer 值, 这样当他们正常访问支付宝网站时, 因为没有提供 Referer 值而被误认为 CERF 攻击, 拒绝访问. 实际应用中通常采用第二种方法来防御 CSRF 攻击.
添加 token 验证
CSRF 攻击之所以能够成功, 是因为攻击者可以完全伪造用户的请求, 该请求中所有的用户验证信息都存在 cookie 中, 因此攻击者可以在不知道这些验证信息的情况下直接利用用户自己的 cookie 来通过安全验证. 要防止 CSRF, 关键在于在请求中放入黑客所不能伪造的信息, 并且该信息不存在于 cookie 之中. 可以在 HTTP 请求中以参数的形式加入一个随机产生的 token, 并在服务器建立一个拦截器来验证这个 token, 如果请求中没有 token 或者 token 不正确, 则认为可能是 CSRF 攻击而拒绝该请求.
现在业界一致的做法就是使用 Anti CSRF Token 来防御 CSRF.
用户访问某个表单页面.
服务端生成一个 Token, 放在用户的 Session 中, 或者浏览器的 Cookie 中.
在页面表单附带上 Token 参数.
用户提交请求后, 服务端验证表单中的 Token 是否与用户 Session(或 Cookies)中的 Token 一致, 一致为合法请求, 不是则非法请求.
这个 Token 值必须是随机的, 不可预测的. 由于 Token 的存在, 攻击者无法再构造一个带有合法 Token 的请求实施 CSRF 攻击. 另外使用 Token 应注意 Token 的保密性, 尽量把敏感操作由 GET 改成 POST, 以 form 或者 AJAX 形式提交, 避免 Token 泄露.
验证码
验证码, 强制用户必须与应用进行交互, 才能完成最终请求. 通常情况下, 验证码能够很好的遏制 CSRF 攻击. 但是出于用户体验考虑, 网站不能给所有的操作都加上验证码. 因此验证码只能作为一种辅助手段.
尽量使用 POST, 限制 GET
GET 接口能够直接将请求地址暴露给攻击者, 所以要防止 CSRF 一定最好不要用 GET. 当然 POST 并不是万无一失, 攻击者只需要构造一个 form 表单就可以, 但需要在第三方页面做, 这样就增加了暴露的可能性.
在 HTTP 头部添加自定义属性
这种方法也是使用 token 并验证, 但是它是把 token 放在 HTTP 请求头部中. 通过使用 AJAX 我们可以在我们的请求头部中添加我们的自定义属性, 但是这种方法要求我们将整个站的请求全部改成 AJAX, 如果是新站还好, 老站的话无疑是需要重写整个站点的, 这是很不可取的.
来源: http://www.qdfuns.com/article/11994/45bd0c2311834f3a3ac1d6244781d382.html