系列文章:
CSRF介绍
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本,但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装成受信任用户的请求来利用受信任的网站。攻击通过在授权用户访问的页面中包含链接或者脚本的方式工作
CSRF攻击
一个典型的CSRF攻击流程大概如下:
- 用户登录
a.com
并保留登录信息 - 攻击者引诱用户访问了
b.com
-
b.com
在用户不知情的情况下向a.com
发送请求并携带用户的登录信息 -
a.com
接收请求验证登录信息通过执行某些恶意操作 - 攻击者在用户不知情的情况下冒充用户的身份完成了攻击.
攻击方式:
- 攻击者的网站
- 有文件上传漏洞的网站
- 第三方论坛,博客等网站
- 目标网站自身的漏洞
相对XSS
攻击,CSRF
攻击不太一样
- 一般攻击发起点不在目标网站,而是被引导到第三方网站再发起攻击,这样目标网站就无法防止
- 攻击者不能获取到用户Cookies,包括子域名,而是利用Cookies的特性冒充用户身份进行攻击
- 通常是跨域攻击,因为攻击者更容易掌握第三方网站而不是只能利用目标网站自身漏洞
- 攻击方式包括图片,URL,CORS,表单,甚至直接嵌入第三方论坛,文章等等,难以追踪
常见的CSRF攻击类型
GET请求
例如利用隐藏图片自动发起一个HTTP请求,会自动附带用户cookies
<img style="width:0;" src="https://www.test.com/xxx" />
POST请求
例如利用隐藏表单自动提交
<form action="https://www.test.com/xxx" method=POST>
<input type="hidden" name="account" value="xiaoming" />
<input type="hidden" name="amount" value="10000" />
</form>
<script> document.forms[0].submit(); </script>
URL攻击
比较常见的利诱广告方式或者冒充QQ病毒警告等引诱用户自己点击
<a href="https://www.test.com/xxx" taget="_blank">
一刀9999级,神级装备,顶级神宠,开服就有!!
<a/>
防御
针对CSRF的特点,我们可以制定策略
限制访问名单
同源检测
HTTP协议中一般会携带两个带有来源信息的字段:
Origin
指示了请求来自于哪个站点。该字段仅指示服务器名称,并不包含任何路径信息, 用于 CORS 请求或者 POST 请求。Origin在以下两种情况下并不存在:
- IE 11 不会在跨站CORS请求上添加Origin标头
- 302重定向之后Origin不包含在重定向的请求中,因为Origin可能会被认为是其他来源的敏感信息。对于302重定向的情况来说都是定向到新的服务器上的URL,因此浏览器不想将Origin泄漏到新的服务器上。
Referer
包含了当前请求页面的来源页面的地址,即表示当前页面是通过此来源页面里的链接进入的。服务端一般使用 Referer 首部识别访问来源,可能会以此进行统计分析、日志记录以及缓存优化等。
- 对于Ajax请求,图片和script等资源请求,Referer为发起请求的页面地址。
- 对于页面跳转,Referer为打开页面历史记录的前一个页面地址
在以下情况下,Referer
不会被发送:
- 来源页面采用的协议为表示本地文件的 "file" 或者 "data" URI
- 当前请求页面采用的是非安全协议,而来源页面采用的是安全协议(HTTPS)
虽然HTTP有明确要求,也有Referrer Policy草案对浏览器如何发送做了详细规定,但是浏览器实现可能有差别,不能保障安全性.低版本浏览器,Flash等情况可能丢失或不可信,新的Referrer规定了五种策略:
States | 作用 |
---|---|
no-Referrer | 任何情况下都不发送Referrer信息 |
no-Referrer-when-downgrade | 仅当协议降级(如HTTPS页面引入HTTP资源)时不发送Referrer信息。是大部分浏览器默认策略 |
origin | 发送只包含host部分的referrer. |
origin-when-cross-origin | 仅在发生跨域访问时发送只包含host的Referer,同域下还是完整的。与Origin Only的区别是多判断了是否Cross-origin。协议、域名和端口都一致,浏览器才认为是同域 |
unsafe-url | 全部都发送Referrer信息。最宽松最不安全的策略 |
设置Referrer Policy的方法有:
-
在HTTP的CSP(Content Security Policy)设置
Content-Security-Policy: referrer no-referrer|no-referrer-when-downgrade|origin|origin-when-cross-origin|unsafe-url;
-
页面头部增加meta标签, 默认no-referer策略
<meta name="referrer" content="no-referrer|no-referrer-when-downgrade|origin|origin-when-crossorigin|unsafe-url">
-
a标签增加Referrer Policy属性,只支持三种
<a href="http://example.com" referrer="no-referrer|origin|unsafe-url">xxx</a>
发起请求的来源域名可能是网站本域,或者子域名,或者有授权的第三方域名,又或者来自不可信的未知域名。业务上需要针对各种情况作出过滤规则,一般优先使用Origin
确认来源信息就够了,Referrer
变数太多比较适合打辅助.但是如果两者都获取不到的情况下,建议直接进行阻止.
同源规则能简单防范大多数CSRF攻击,配合关键接口做额外处理能更好提高安全性.
SameSite
一种新的防止跨站点请求伪造(cross site request forgery)的 http 安全特性。该值可以设置为 Strict
或 Lax
,现阶段只有部分主流浏览器支持,仅做了解即可
Set-Cookie: key=value; SameSite=Strict/Lax
- Strict: 跨域请求或者新标签重新打开都不会携带该Cokies
- Lax: 这个请求是(改变了当前页面或者打开了新页面)且同时是个GET请求,则携带。
还有一个比较严重的问题是SameSite不支持子域名.
附加验证
验证码
通过图形验证码或者手机验证码或者邮箱验证等多种方式强制用户进行交互可以有效遏制CSRF攻击,缺点是步骤比较繁琐,只适用于如涉及金额,密码相关等关键请求,
CSRF Token
基于攻击者无法获得用户信息的特性,我们可以在前后端交互中携带一个有效验证“令牌”来防范CSRF攻击,大概流程:
- 当用户首次登录成功之后, 服务端会生成一个唯一性和随机性的 token 值保存在服务器的Session或者其他缓存系统中,再将这个token值返回给浏览器;
- 浏览器拿到 token 值之后本地保存;
- 当浏览器再次发送网络请求的时候,就会将这个 token 值附带到参数中(或者通过Header头)发送给服务端;
- 服务端接收到浏览器的请求之后,会取出token值与保存在服务器的Session的token值做对比验证其正确性和有效期。
在大型网站一般使用多台服务器,用户请求经过负载均衡器路由到具体的服务器上,如果使用Session默认储存在单机服务器内存中,在分布式环境下同一用户的多次请求可能会指向不同的服务器上,而其他的服务器无法共享Session导致Session机制失效无法验证,所以分布式集群中Token需要储存在Redis等公共储存空间.
因为读取和验证Token会有复杂度和性能的问题,还有种方式采用Encrypted Token Pattern方式,通常是使用UserID、时间戳和随机数,通过加密的方法生成而非随机性,之后请求校验不需要读取而是直接计算即可,这样既可以保证分布式服务的Token一致,又能保证Token不容易被破解。
双重Cookie验证
相较于CSRF Token,这种方式比较简单实现但是安全性较低.大概流程:
- 用户访问页面之后域名被注入随机字符串Cookie
- 浏览器发起请求时会取出该Cookie字符串添加到URL参数中
- 服务端验证是否一致
没有大规模应用除了安全性问题还有一个就是跨域可能导致获取不到Cookie.
- 用户访问网站域名
www.test.com
,服务端api域名api.test.com
, - 如果想要共用Cookie就必须注入到
test.com
,然后子域名都能获取到 - 同理每个子域名都能修改该Cookie,如果某个子域名被攻击了
- 攻击者可以自己配置一个Cookie破解双重Cookie验证机制拦截
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。