# 跨站请求伪造 (CSRF)
# 1. 介绍
CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。
攻击者盗用了你的身份,以你的名义发送恶意请求。对服务器来说这个请求是完全合法的,但却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。
# CSRF攻击原理及过程
- 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
- 在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
- 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
- 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
- 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。

# Cookie的主要特性
- 浏览器默认自动携带本次HTTP请求域名的Cookie(不管是通过什么方式,在什么页面发送的HTTP请求)
- 读写Cookie有跨域限制(作用域,Domain,Path)
- 生命周期(会话or持久)
# 2. 攻击对象
- 个人用户:盗取用户资金(包括虚拟货币)、操控用户账户等。
- 企业用户:盗取公司资金、查询内部数据、删除数据、更改公司网站等。
# 3. 特点
- 依靠用户登录网站后获取的Cookie进行攻击,当用户没有退出网站时,攻击就会一直存在
- 攻击者不能直接获取到Cookie等信息,只是盗用
- 跨站请求伪造攻击具有隐蔽性,用户甚至在毫不知情的情况下帮助攻击者执行了攻击操作
# 4. 防范措施
上文中讲了CSRF的两个特点:
CSRF(通常)发生在第三方域名
CSRF攻击者不能获取到Cookie等信息,只是使用
针对这两点,我们可以专门制定防护策略:
阻止不明外域的访问
提交时要求附加本域才能获取的信息
# 4.1 验证 HTTP Referer 字段
根据 HTTP 协议,在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址。也就是说,服务器会验证客户端的请求来源,如果本网站请求的则响应,否则不响应。
处理了下面几种特殊情况,用Referrer防CSRF是安全
- 读操作不能有上面提到提到的两种特殊情况,不能用JSONP,CORS要白名单,所以读操作是安全的。
- 写操作Referrer为空的时候不能放过,使用白名单机制,Referrer在白名单内才放过。什么时候会为空?
- 地址栏直接输入url的时候,第一个请求Referrer为空,一般是html页面,这种读操作不用防CSRF
- 使用Referrer-Policy策略设置no-referrer是,Referrer为空,自己的页面不要这样设置,为了防黑客的页面设置了,所以为空的时候不能放过。
- 还有一些iframe的特殊使用(以前用来绕过图片防盗链的)也会导致Referrer为空,这些情况都不能放过过。
- 写操作不能用Get,如果写操作可以用Get,由于Img标签,A标签能发Get请求。所以在一些UGC网站,比如用户写日志可以插入自定义图片,能插入自定义连接,图片img标签src或者A标签的href就指向写操作的URL,这样只要打开这篇日志就会发送这个Get请求,或者点击了日志上的连接,就帮用户做了写操作。并且Referrer还是合法的。这就是一种少见的CSRF攻击过程,其实也是最早期的CSRF攻击。这种攻击一旦成功,很方便做成蠕虫病毒,危害性极大。PS有人觉得这种少见的攻击过程不算CSRF,应该算XSS,好像也有点道理,但是常规的防XSS的方法貌似不好防这种特殊情况,下面要讲的CSRF的Token的防护方式是能防这种特殊情况的。
同理适用Origin:
原理跟Referrer一样,Origin请求头是XHR2.0里增加的,含义是发送请求页面的域名,主要目的是解决跨域问题。如果用来校验CSRF请求,就有一些细节要处理好,后台判断Origin时也要使用白名单,并且不能为空,不在白名单内的请求都直接返回失败,不能执行请求里的写操作(有一些web server是请求执行了,也返回了数据,只是没有配ACAO响应头,浏览器收不到,这种情况能限制跨域请求,但是不能防CSRF的写操作)。另外一种做法就是自定义HTTP请求头,把HTTP请求升级为复杂请求,这样在跨域情况下就会先发一个Option预检请求,预检请求通不过也就不会执行后面真实请求了。
# 4.2 使用 token (Anti CSRF Token)
现在业界对CSRF的防御,一致的做法是使用一个Token(Node、PHP、Java等服务端框架对CSRF的防御都已经有内建的方案)。
CSRF攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于Cookie中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的Cookie来通过安全验证。
要抵御CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于Cookie之中。可以在用户访问网站页面时,向页面注入一个Token,然后再用户提交数据时,将Token一同提交给服务器,服务器进行对比,如果Token不一致,则拒绝执行。
具体流程如下:
- 用户访问某个表单页面。
- 服务端生成一个Token,放在用户的Session中,或者浏览器的Cookie里。
- 在页面表单附带上Token参数。
- 用户提交请求后, 服务端验证表单中的Token是否与用户Session(Cookie)中的Token一致,一致为合法请求,不是则拒绝请求。
Token就是一个令牌,当用户提交请求时,这个Token会告诉服务器:“嘿,是我自己本人发起的请求,不是黑客伪造的哦!”
攻击者可以伪造表单,但是他没有办法伪造Token,因为Token是放在Session或者Cookie里的,攻击者无法获取。
# 4.3 验证码
验证码被认为是对抗CSRF攻击最简洁而有效的防御方法。
CSRF攻击的过程,往往是在用户不知情的情况下构造了网络请求。而验证码会强制用户必须与应用进行交互,才能完成最终请求。因为通常情况下,验证码能够很好地遏制CSRF攻击。
但是出于用户体验考虑,网站不能给所有的操作都加上验证码。因此,验证码只能作为一种辅助手段,不能作为主要解决方案。
# 4.4 SameSite限制
Chrome 51 开始,浏览器的 Cookie 新增加了一个SameSite属性,用来防止 CSRF 攻击和用户追踪。
Cookie 的SameSite属性用来限制第三方 Cookie,从而减少安全风险,但是客户端对SameSite属性的支持并不是特别好
它可以设置三个值。
- Strict
- Lax
- None
# 4.4.1 Strict
Strict最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。
# 4.4.2 Lax
Lax规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。
导航到目标网址的 GET 请求,只包括三种情况:链接,预加载请求,GET 表单。
<a href="..."></a>
<link rel="prerender" href="..."/>
<form method="GET" action="...">
2
3
设置了Strict或Lax以后,基本就杜绝了 CSRF 攻击。当然,前提是用户浏览器支持 SameSite 属性
# 4.4.3 None
Chrome 计划将Lax变为默认设置。这时,网站可以选择显式关闭SameSite属性,将其设为None。不过,前提是必须同时设置Secure属性(Cookie 只能通过 HTTPS 协议发送),否则无效
# 5. 参考资料
web安全之csrf攻击 (opens new window)
← XSS DOM事件冒泡-捕获-委托 →