https://juejin.im/post/5bc009996fb9a05d0a055192
这个页面只要打开,就会向Gmail发送一个post请求。请求中,执行了“Create Filter”命令,将所有的邮件,转发到“hacker@hakermail.com”。小明由于刚刚就登陆了Gmail,所以这个请求发送时,携带着小明的登录凭证(Cookie),Gmail的后台接收到请求,验证了确实有小明的登录凭证,于是成功给小明配置了过滤器。黑客可以查看小明的所有邮件,包括邮件里的域名验证码等隐私信息。拿到验证码之后,黑客就可以要求域名服务商把域名重置给自己。小明很快打开Gmail,找到了那条过滤器,将其删除。然而,已经泄露的邮件,已经被转让的域名,再也无法挽回了......
什么是CSRF
CSRF(Cross-site request forgery)跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
一个典型的CSRF攻击有着如下的流程:
受害者登录a.com,并保留了登录凭证(Cookie)。
b.com 向 a.com 发送了一个请求:a.com/act=xx。浏览器会…
a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求。
a.com以受害者的名义执行了act=xx。
攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让a.com执行了自己定义的操作。
几种常见的攻击类型
GET类型的CSRF
GET类型的CSRF利用非常简单,只需要一个HTTP请求,一般会这样利用:
在受害者访问含有这个img的页面后,浏览器会自动向http://bank.example/withdraw?account=xiaoming&amount=10000&for=hacker发出一次HTTP请求。bank.example就会收到包含受害者登录信息的一次跨域请求。
POST类型的CSRF
这种类型的CSRF利用起来通常使用的是一个自动提交的表单
访问该页面后,表单会自动提交,相当于模拟用户完成了一次POST操作。
POST类型的攻击通常比GET要求更加严格一点,但仍并不复杂。任何个人网站、博客,被黑客上传页面的网站都有可能是发起攻击的来源,后端接口不能将安全寄托在仅允许POST上面。
链接类型的CSRF
链接类型的CSRF并不常见,比起其他两种用户打开页面就中招的情况,这种需要用户点击链接才会触发。这种类型通常是在论坛中发布的图片中嵌入恶意链接,或者以广告的形式诱导用户中招,攻击者通常会以比较夸张的词语诱骗用户点击,例如:
重磅消息!!复制代码
由于之前用户登录了信任的网站A,并且保存登录状态,只要用户主动访问上面的这个PHP页面,则表示攻击成功。
CSRF的特点
攻击一般发起在第三方网站,而不是被攻击的网站。被攻击的网站无法防止攻击发生。
攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作;而不是直接窃取数据。
整个过程攻击者并不能获取到受害者的登录凭证,仅仅是“冒用”。
跨站请求可以用各种方式:图片URL、超链接、CORS、Form提交等等。部分请求方式可以直接嵌入在第三方论坛、文章中,难以进行追踪。
CSRF通常是跨域的,因为外域通常更容易被攻击者掌控。但是如果本域下有容易被利用的功能,比如可以发图和链接的论坛和评论区,攻击可以直接在本域下进行,而且这种攻击更加危险。
防护策略
CSRF通常从第三方网站发起,被攻击的网站无法防止攻击发生,只能通过增强自己网站针对CSRF的防护能力来提升安全性。
上文中讲了CSRF的两个特点:
CSRF(通常)发生在第三方域名。
CSRF攻击者不能获取到Cookie等信息,只是使用。
针对这两点,我们可以专门制定防护策略,如下:
阻止不明外域的访问:同源检测、Samesite Cookie
提交时要求附加本域才能获取的信息:CSRF Token、双重Cookie验证
CSRF Token
前面讲到CSRF的另一个特征是,攻击者无法直接窃取到用户的信息(Cookie,Header,网站内容等),仅仅是冒用Cookie中的信息。
而CSRF攻击之所以能够成功,是因为服务器误把攻击者发送的请求当成了用户自己的请求。那么我们可以要求所有的用户请求都携带一个CSRF攻击者无法获取到的Token。服务器通过校验请求是否携带正确的Token,来把正常的请求和攻击的请求区分开,也可以防范CSRF的攻击。
原理
CSRF Token的防护策略分为三个步骤:
1.将CSRF Token输出到页面中
首先,用户打开页面的时候,服务器需要给这个用户生成一个Token,该Token通过加密算法对数据进行加密,一般Token都包括随机字符串和时间戳的组合,显然在提交时Token不能再放在Cookie中了,否则又会被攻击者冒用。因此,为了安全起见Token最好还是存在服务器的Session中,之后在每次页面加载时,使用JS遍历整个DOM树,对于DOM中所有的a和form标签后加入Token。这样可以解决大部分的请求,但是对于在页面加载之后动态生成的HTML代码,这种方法就没有作用,还需要程序员在编码时手动添加Token。
2.页面提交的请求携带这个Token
对于GET请求,Token将附在请求地址之后,这样URL 就变成 http://url?csrftoken=tokenvalue。 而对于 POST 请求来说,要在 form 的最后加上:
这样,就把Token以参数的形式加入请求了。
3.服务器验证Token是否正确
当用户从客户端得到了Token,再次提交给服务器的时候,服务器需要判断Token的有效性,验证过程是先解密Token,对比加密字符串以及时间戳,如果加密字符串一致且时间未过期,那么这个Token就是有效的。
双重Cookie验证
在会话中存储CSRF Token比较繁琐,而且不能在通用的拦截上统一处理所有的接口。
那么另一种防御措施是使用双重提交Cookie。利用CSRF攻击不能获取到用户Cookie的特点,我们可以要求Ajax和表单请求携带一个Cookie中的值。
双重Cookie采用以下流程:
在用户访问网站页面时,向请求域名注入一个Cookie,内容为随机字符串(例如csrfcookie=v8g9e4ksfhw)。
在前端向后端发起请求时,取出Cookie,并添加到URL的参数中(接上例POST https://www.a.com/comment?csrfcookie=v8g9e4ksfhw)。
后端接口验证Cookie中的字段与URL参数中的字段是否一致,不一致则拒绝。
总结
用双重Cookie防御CSRF的优点:
无需使用Session,适用面更广,易于实施。
Token储存于客户端中,不会给服务器带来压力。
相对于Token,实施成本更低,可以在前后端统一拦截校验,而不需要一个个接口和页面添加。
缺点:
Cookie中增加了额外的字段。
如果有其他漏洞(例如XSS),攻击者可以注入Cookie,那么该防御方式失效。
难以做到子域名的隔离。
为了确保Cookie传输安全,采用这种防御方式的最好确保用整站HTTPS的方式,如果还没切HTTPS的使用这种方式也会有风险。
Samesite Cookie属性
防止CSRF攻击的办法已经有上面的预防措施。为了从源头上解决这个问题,Google起草了一份草案来改进HTTP协议,那就是为Set-Cookie响应头新增Samesite属性,它用来标明这个 Cookie是个“同站 Cookie”,同站Cookie只能作为第一方Cookie,不能作为第三方Cookie,Samesite 有两个属性值,分别是 Strict 和 Lax
防止网站被利用
前面所说的,都是被攻击的网站如何做好防护。而非防止攻击的发生,CSRF的攻击可以来自:
攻击者自己的网站。
有文件上传漏洞的网站。
第三方论坛等用户内容。
被攻击网站自己的评论功能等。
对于来自黑客自己的网站,我们无法防护。但对其他情况,那么如何防止自己的网站被利用成为攻击的源头呢?
严格管理所有的上传接口,防止任何预期之外的上传内容(例如HTML)。
添加Header X-Content-Type-Options: nosniff 防止黑客上传HTML内容的资源(例如图片)被解析为网页。
对于用户上传的图片,进行转存或者校验。不要直接使用用户填写的图片链接。
当前用户打开其他用户填写的链接时,需告知风险(这也是很多论坛不允许直接在内容中发布外域链接的原因之一,不仅仅是为了用户留存,也有安全考虑)。
CSRF其他防范措施
对于一线的程序员同学,我们可以通过各种防护策略来防御CSRF,对于QA、SRE、安全负责人等同学,我们可以做哪些事情来提升安全性呢?
CSRF测试
CSRFTester是一款CSRF漏洞的测试工具,CSRFTester工具的测试原理大概是这样的,使用代理抓取我们在浏览器中访问过的所有的连接以及所有的表单等信息,通过在CSRFTester中修改相应的表单等信息,重新提交,相当于一次伪造客户端请求,如果修改后的测试请求成功被网站服务器接受,则说明存在CSRF漏洞,当然此款工具也可以被用来进行CSRF攻击。 CSRFTester使用方法大致分下面几个步骤:
步骤1:设置浏览器代理 步骤2:使用合法账户访问网站开始测试
步骤3:通过CSRF修改并伪造请求 步骤4:拿到结果如有漏洞进行修复
CSRF监控
对于一个比较复杂的网站系统,某些项目、页面、接口漏掉了CSRF防护措施是很可能的。
一旦发生了CSRF攻击,我们如何及时的发现这些攻击呢?
CSRF攻击有着比较明显的特征:
跨域请求。
GET类型请求Header的MIME类型大概率为图片,而实际返回Header的MIME类型为Text、JSON、HTML。
我们可以在网站的代理层监控所有的接口请求,如果请求符合上面的特征,就可以认为请求有CSRF攻击嫌疑。我们可以提醒对应的页面和项目负责人,检查或者 Review其CSRF防护策略。
个人用户CSRF安全的建议
经常上网的个人用户,可以采用以下方法来保护自己:
使用网页版邮件的浏览邮件或者新闻也会带来额外的风险,因为查看邮件或者新闻消息有可能导致恶意代码的攻击。
尽量不要打开可疑的链接,一定要打开时,使用不常用的浏览器。
总结
简单总结一下上文的防护策略:
CSRF自动防御策略:同源检测(Origin 和 Referer 验证)。
CSRF主动防御措施:Token验证 或者 双重Cookie验证 以及配合Samesite Cookie。
保证页面的幂等性,后端接口不要在GET页面中做用户操作。
为了更好的防御CSRF,最佳实践应该是结合上面总结的防御措施方式中的优缺点来综合考虑,结合当前Web应用程序自身的情况做合适的选择,才能更好的预防CSRF的发生。