前端安全之 CSRF 攻击原理和防护方法
Posted 不爱青椒
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端安全之 CSRF 攻击原理和防护方法相关的知识,希望对你有一定的参考价值。
CSRF(Cross-site request forgery)简称:跨站请求伪造,学习 CSRF 攻击原理和防护方法是我们团队新成员的必修课,通常我都是先让新同学自己研究自己讲,然后我再通过其中细节再给他们讲一遍,讲的次数多了,也慢慢总结出一种比较容易理解的讲法。这里我整理成文分享给有需要的人。
引言:必修课为什么选择 CSRF?
-
CSRF 涉及到的前端知识点比较多,全面理解需要系统的学习 Cookie,前端跨域,HTTP 协议,web 浏览器等知识。
-
理解 CSRF 攻击和防护方法是前端进阶要去,我也希望大家都能够掌握。
-
个人兴趣,我个人对前端性能优化和前端安全比较感兴趣,也算参与和推动了公司内的 CSRF 防护方案从无到有的过程。
一、CSRF 攻击原理
1、Cookie 的使用
HTTP 是无状态协议,服务器只能根据当前请求的参数(包括 Head 和 Body 的数据)来判断本次请求需要达到的目的(Get 或者 Post 都一样),服务器并不知道这个请求之前干了什么事,所以这就是无状态协议。
但是我们现实很多情况需要有状态,比如登录态的身份信息,网页上很多操作需要登录之后才能操作,比如相册的上传照片和删除照片等功能都需要登录之后才能操作。那么怎么判断登录态?(通常我会问如果你遇到这个问题你回怎么做?),其实解决方案都是类似的,只能是每次 HTTP 请求都要把登录态信息(这里用 Key 表示)传给后台服务器,后台通过 Key 字段是判断用户合法性之后再处理这个请求要处理的敏感操作。
怎么方便的让每次 HTTP 请求都带上 Key?所以就设计出了 Cookie,这里列举 Cookie 主要的一些特性。
-
浏览器默认自动携带本次 HTTP 请求域名的 Cookie(不管是通过什么方式,在什么页面发送的 HTTP 请求)。
-
读写 Cookie 有跨域限制(作用域,Domain,Path)。
-
生命周期(会话 or 持久)。
2、CSRF 的攻击过程
根据上面介绍,登录态 Cookie 的 Key 是浏览器默认自动携带的,Key 通常是会话 Cookie,只要浏览器不关闭,Key 一直存在。所以只要用户 A 曾经登录过相册网站(这里用 http://www.photo.com 举例),浏览器没有关闭,用户在没有关闭的浏览器打开一个黑客网页(这里用 http://www.hacker.com),黑客页面发送 HTTP 请求到 http://www.photo.com 的后台会默认带上 http://www.photo.com 的登录态 Cookie,也就能模拟用户 A 做一些增删改等敏感操作。Get 和 Post 都一样,这就是 CSRF 攻击原理。这种攻击过程也是最常见的攻击过程,后面还会介绍另外一种少见的攻击过程。
3、读操作能否被攻击到?
上面说的增删改都是写操作,会对后台数据产生负面影响,所以是能被攻击的。另外一种读操作,是具有幂等性,不会对后台数据残生负面影响,能否被攻击到?读操作也可能是敏感数据,举个例子,比如www.photo.com上的私密相册数据能否被www.hacker.com页面拿到?这就涉及到前端跨域知识点了,默认大部分情况是拿不到,这里列举两种特殊情是可以拿到的:
-
如果后台返回的数据是 JSONP 格式的,这种只能是 Get 操作,是能被黑客页面拿到的。
-
如果后台是通过 CORS 处理跨域,没有对请求头的 Origin 做白名单限制,ACAO 响应头是*或者包括黑客页面,包括 Get/Post/Del 等操作,也是能被黑客页面拿到的。
除了这两种特殊情况,读操作都是不能被攻击到的,因为浏览器跨域限制是天然的安全的。
二、CSRF 防护方法
知道攻击原理,防护方法也很简单,找到能够区分请求发送的页面是自己的页面还是黑客的页面的方法就可以了。
1、Referrer
HTTP 请求头 Referrer 字段是浏览器默认带上,含义是发送请求的页面地址,比如同样是删除相册的操作http://www.photo.com/del?id=xxx;如果是从相册自己页面发送出来,Referrre的值是http://www.photo.com/index.html(以首页举例),如果是从黑客页面发送出来的Referrer的值是http://www.hacker.com/index.html(以首页举例),所以后端只要通过Referrrer做白名单判断就能防这种常见的CSRF攻击。下面探讨几个容易被忽悠的问题。
1.1 Referrer 会不会被伪造或者篡改?
-
在浏览器环境下,Referrer 是浏览器自己带上的,js 是改不了 Rerferrer,所以是不能被伪造和篡改的。
-
浏览器插件能改 Referrer 么?答案:能改,但是浏览器插件攻击不属于 CSRF 攻击范畴,如果用户浏览器都已经被安装了黑客插件了就有更方便的攻击方法,但是不可能在所有用户浏览器都安装上黑客的插件。
-
通过网关或者抓包修改 Referrer?答案:能改,这是中间人攻击,也不属于 CSRF 攻击范畴。防中间人攻击用 HTTPS。
-
黑客通过自己后台代理,请求发到黑客自己的后台,黑客后台修改 Referrer 再转发到相册后台,可以改么?可行么?答案:能改,但不可行,请求发送到黑客自己后台不会带上相册的 Cookie,登录态校验通不过,敏感操作做不了。
1.2 用 Referrer 防 CSRF 安全么?
处理了下面几种特殊情况,用 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 的防护方式是能防这种特殊情况的。
1.3 用 Origin 可以么?
可以,原理跟 Referrer 一样,Origin 请求头是 XHR2.0 里增加的,含义是发送请求页面的域名,主要目的是解决跨域问题。如果用来校验 CSRF 请求,就有一些细节要处理好,后台判断 Origin 时也要使用白名单,并且不能为空,不在白名单内的请求都直接返回失败,不能执行请求里的写操作(有一些 web server 是请求执行了,也返回了数据,只是没有配 ACAO 响应头,浏览器收不到,这种情况能限制跨域请求,但是不能防 CSRF 的写操作)。另外一种做法就是自定义 HTTP 请求头,把 HTTP 请求升级为复杂请求,这样在跨域的情况就会先发一个 Option 预检请求,预检请求通不过也就不会执行后面真实请求了。
1.4 既然能做到安全,为什么现在很少见用这种方案?
因为有更简单的方案,就是下面要讲的 Token 方案。
2、Token
上面讲到 Cookie 的一些特性的第二条,读写 Cookie 有跨域限制(作用域,Domain,Path),所以我们可以用这个特性来区分是自己页面还是黑客页面。只要页面能读(或者写)www.photo.com域名 Cookie,就证明是自己的页面。懂了原理,方案就很简单,比如服务器通过 cookie 下发一个 token,token 值是随机数,页面发请求的时候从 cookie 取出 token 通过 HTTP 请求参数传给后台,后台比对参数里的 token 和 cookie 里的 token 是否一致,如果一致就证明是自己页面发的请求,如果不一致就返回失败。防 CSRF 的方案就是这么简单,这种方法能够 100%防 CSRF,但是可能会有几个变种,下面探讨几种情况。
2.1 Token 是前台生产还是后台生产?
我上面举例例子是后台生成传到前台的,大家发现其实后台并没有存这个 token,所以原理上前后台生成都可以,只要保证随机性。如果前端生成 token 然后写到 Cookie 里,然后 HTTP 请求参数也带上 token,后端逻辑一样比对参数里的 token 和 cookie 里的 token 是否一致,如果一致就证明是自己页面发的请求,如果不一致就返回失败。这就是 Cookie 读和写的差别,只要能读写自己域名的 Cookie 就是自己页面。
2.2 推荐的最佳实践方案
由于登录态已经下发了一个登录态 key,防 CSRF 的 token 就复用这个 key,由于登录态 key 比较重要,尽量少明文暴露,所以前端拿到 key 后做了一次 Hash 放到 http 请求参数里,后端通过同样的 Hash 算法对 Cookie 里的 key 做 Hash 后跟参数里的 token 比对是否一致,如果一致就证明是自己页面发的请求,如果不一致就返回失败。这里对 Hash 算法要求不高,简单高效就可以。
2.3 Token 放在 HTTP 参数里的哪里?
放在 URL 的 querystring 里,Post 请求的 Data 里或者 HTTP 请求头里,这三种方式都可以,只是有一点点细微的差别,如果 querystring 里,可能会影响 Get 请求的缓存效果,因为重新登录之后 token 会变,url 也就变了,之前的缓存就失效了。如果放在 HTTP 请求头里,就需要使用 fetch 或者 XHR 发请求,这样会变成复杂请求,跨域时需要多一次 Option 预检请求,对性能多少有一点点影响。
2.4 用 Token 方案后写操作可以用 Get 么?
可以,从安全角度考虑是可以的,用了 Token 之后,Get 和 Post 的安全等级是一样的,上面讨论的那种少见的 CSRF 攻击过程也攻击不到了。但是从语义化考虑建议 Get 是还是处理读操作方便理解。
2.5 用 Token 方案后读操作可以用 JSONP 跨域么?
可以,可以使用 JSONP 跨域了,另外如果使用 CORS 处理跨域,建议还是需要对请求头的 Origin 做白名单限制,防止不同子域名相互影响。
2.6 如果页面有 XSS 漏洞,黑客拿到 Cookie 怎么办?
这个方法防不了 XSS,防 XSS 需要其他方法,比如 CSP,用户输入/输出做转义等。
3、是否还有其他方案
3.1 Cookie 的 SameSite 属性可以么?
不好用,SameSite 设计的目的貌似就是防 CSRF,但是我觉得不好用,SameSite 有三个值 Strict/Lax/None,设置的太严格,会影响自己业务的体验,设置的太松没有效果,就算最严格 Strict 模式,也防不了我上面提到写操作用 Get 请求,UGC 页面有自定义照片的情况。并且还有小部分老浏览器不支持,最终其实还是 Token 方案好用。
3.2 Cookie 的 HTTPOnly 属性可以么?
不行,HTTPOnly 表示这个 Cookie 只能是 HTTP 请求可以读写,js 没有读写权限,浏览器还是会默认带上,所以登录态校验是通过的。如果设置了 HTTPOnly 还有副作用,上面说的 Token 方案就用不了了。
3.3 验证码可以么?
不行,验证码是用来防机器暴力攻击的,验证码是用来确认敏感操作是自然人发送还是机器自动发送。这里举个图片验证码的例子,大概原理是前端通过 img 标签展示图片验证码给用户看(图片字母经过噪音处理的),这个图片 HTTP 请求也会设置一个 cookie 如 codeID=xxx(加密的),用户在输入框输入图片中展示的字母,敏感操作的 HTTP 请求通过参数把用户输入的 code 传给后台,后台拿到用户输入的 code 和 cookie 里的 codeID(通常需要通过 id 查数据库)做比较,如果一致就通过。这种验证码系统能够防机器攻击,但是防不了 CSRF,黑客同样可以在黑客的页面展示验证码给用户,通过诱导用户输入验证码完成攻击操作,只能是提高了 CSRF 攻击成功的门槛,但是只要黑客页面诱导信息劲爆还是有很大部分用户会上当的。因为用户不知道输入验证码后会产生什么影响。
验证码我在一些资料上看到说可以用来防 CSRF,我个人觉得是不行,包括手机验证码都不行,详细情况大家可以研究各种验证码的实现原理。我猜测有些人可能有不同意见,但是如果非要构造一种能防 CSRF 的验证码技术上也是可行的。我这里推演一下防护过程。就拿我上面举得验证码举例。验证码图片的 url 是固定格式的http://code.photo.com/codeImg.jpg?v=123。v是随机数,换一张时防止缓存用的。验证码每次请求会种一个Cookie,codeID=xxx,后台会存储这个codeID对应的真实code,用户输入图片看到验证码,要校验验证码的请求参数会带上用户输入的code,后台拿到参数code和Cookie里的CodeID查数据库后对比来判断是否输入正确。攻击方式,黑客可以在黑客页面用img标签展示这个验证码,因为验证码url是固定格式的,后面的流程是一样的。你可能的改进方案:
-
验证码图片做防盗链。黑客破解方案:可以用 Referrer-Policy 的 no-referrer
-
no-referrer 不给通过。黑客破解方案:可以用 iframe 嵌入你自己的页面,只把你自己页面种的验证码区域展示出来
-
我的页面不给 iframe 嵌入。黑客说,你成功的防住了
所以需要对验证码做(1)(2)(3)的改造才能防 CSRF 攻击。其实加的(1)(2)(3)措施都不是验证码的本意,验证码是本意用来的防机器攻击的,不加(1)(2)(3)措施也一样可以防机器攻击。这里就有三种观点了。观点一:我只要找到一个反例,找到验证码不能防 CSRF 的一个例子,我就证明了验证码不能防 CSRF。观点二:我只要构造一个能防的例子,对验证码做一系列额外的改造来防 CSRF,我就证明了验证码能防 CSRF。观点三:验证码提高了攻击门槛,攻防就是魔高一丈道高一尺的过程,加上验证码更安全。我个人赞成观点一。读者们你们觉得呢?
3.4 HTTPS 可以么?
不行,HTTPS 是防中间人攻击的,不是防 CSRF 的
3.5 不用 Cookie 可以么?
可以。个人觉得非常不好用,这里讨论两种方案。
方案一:登录态 key 不放在 Cookie。所以 HTTP 请求也不会自动携带 key,也就不存在 CSRF 漏洞,也就不用防了。但是这种设计我个人觉得在一些大型复杂网站是非常棘手难搞的,因为涉及到新开页面,多个页面之间登录态需要同步(其中一个页面退出登录,登录另外账户,或者登录态过期续期等都需要同步给其他页面),跨页面通讯也有好多方案,如果你使用 localstorage 等本地缓存的话,关闭页面还要清理缓存,缓存满了要清理,浏览器兼容问题等。这种大型系统还会遇到其他的一系列问题,也会有一些列的解决方案,系统会比较复杂,最终还不如用 Cookie 方便。
方案二:登录态 key 放 Cookie。CSRF 的 Token 不放 Cookie,后台生成 Token 藏在 HTML 页面里,后台也存了这个 Token。HTTP 请求通过参数带上这个 Token,后台拿到参数里的 Token 跟自己后台存的 Token 做校验。这个方案也是在一些资料里看到的。但是这个方案也是相当复杂,比如这个方案需要处理好几个关键问题,这个 Token 是有用户属性,要跟用户绑定的。如果 Token 跟用户无关,那么黑客可以用自己账户的 Token 欺骗做水平攻击。另外也有新开页面,多个页面是同一个 Token 还是不同 Token?如果不同 Token,后台需要存一个 Token 列表,列表长度有最大值?另外还有如果其中一个页面退出登录,再登录另外账户。那么其他页面登录态是同步了,但是 Token 如何同步?同样你也会有一些列的解决方案。当你把这些问题都解决了,最后你发现既然 Token 有用户属性,那么就可以当登录态用,就不用 Cookie 的登录态 key 了,又回到了方案一。
前端安全之XSS和csrf攻击
1.Csrf攻击概念:
csrf攻击(Cross-site request forgery):跨站请求伪造;
2.Csrf攻击原理:
用户是网站A的注册用户,且登录进去,于是网站A就给用户下发cookie。
从上图可以看出,要完成一次CSRF攻击,受害者必须满足两个必要的条件:
(1)登录受信任网站A,并在本地生成Cookie。(如果用户没有登录网站A,那么网站B在诱导的时候,请求网站A的api接口时,会提示你登录)
(2)在不登出A的情况下,访问危险网站B(其实是利用了网站A的漏洞)。
我们在讲CSRF时,一定要把上面的两点说清楚。
温馨提示一下,cookie保证了用户可以处于登录状态,但网站B其实拿不到 cookie。
举个例子,前端事假你,微博网站有个api接口有漏洞,导致很多用户的粉丝暴增。
3.Csrf如何防御
方法一、Token 验证:(用的最多)
(1)服务器发送给客户端一个token;
(2)客户端提交的表单中带着这个token。
(3)如果这个 token 不合法,那么服务器拒绝这个请求。
方法二:隐藏令牌:
把 token 隐藏在 http 的 head头中。
方法二和方法一有点像,本质上没有太大区别,只是使用方式上有区别。
方法三、Referer 验证:
Referer 指的是页面请求来源。意思是,只接受本站的请求,服务器才做响应;如果不是,就拦截。
4.XSS
1、XSS的基本概念
XSS(Cross Site Scripting):跨域脚本攻击。
接下来,我们详细讲一下 XSS 的内容。
预备知识:HTTP、Cookie、Ajax。
2XSS的攻击原理
XSS攻击的核心原理是:不需要你做任何的登录认证,它会通过合法的操作(比如在url中输入、在评论框中输入),向你的页面注入脚本(可能是js、hmtl代码块等)。
最后导致的结果可能是:
盗用Cookie破坏页面的正常结构,插入广告等恶意内容D-doss攻击XSS的攻击方式
1、反射型发出请求时,XSS代码出现在url中,作为输入提交到服务器端,服务器端解析后响应,XSS代码随响应内容一起传回给浏览器,最后浏览器解析执行XSS代码。这个过程像一次反射,所以叫反射型XSS。
2、存储型存储型XSS和反射型XSS的差别在于,提交的代码会存储在服务器端(数据库、内存、文件系统等),下次请求时目标页面时不用再提交XSS代码。
XSS的防范措施(encode + 过滤)
3、XSS的防范措施主要有三个:
1、编码:
对用户输入的数据进行
HTML Entity
编码。
如上图所示,把字符转换成 转义字符。
Encode的作用是将
$var
等一些字符进行转化,使得浏览器在最终输出结果上是一样的。
比如说这段代码:
<script>alert(1)</script>
若不进行任何处理,则浏览器会执行alert的js操作,实现XSS注入。
进行编码处理之后,L在浏览器中的显示结果就是
<script>alert(1)</script>
,实现了将$var作为纯文本进行输出,且不引起JavaScript的执行。
参考链接:4类防御XSS的有效方法
2、过滤:
移除用户输入的和事件相关的属性。如onerror可以自动触发攻击,还有onclick等。(总而言是,过滤掉一些不安全的内容)移除用户输入的Style节点、Script节点、Iframe节点。(尤其是Script节点,它可是支持跨域的呀,一定要移除)。3、校正
避免直接对HTML Entity进行解码。使用DOM Parse转换,校正不配对的DOM标签。备注:我们应该去了解一下
DOM Parse
这个概念,它的作用是把文本解析成DOM结构。
比较常用的做法是,通过第一步的编码转成文本,然后第三步转成DOM对象,然后经过第二步的过滤。
还有一种简洁的答案:
首先是encode,如果是富文本,就白名单。
CSRF 和 XSS 的区别
面试官还可能喜欢问二者的区别。
区别一:
CSRF:需要用户先登录网站A,获取 cookie。XSS:不需要登录。区别二:(原理的区别)
CSRF:是利用网站A本身的漏洞,去请求网站A的api。XSS:是向网站 A 注入 JS代码,然后执行 JS 里的代码,篡改网站A的内容。
以上是关于前端安全之 CSRF 攻击原理和防护方法的主要内容,如果未能解决你的问题,请参考以下文章