CSRF攻防
早在2018年就了解CSRF,偶然间注意到项目的VerifyCsrfToken中间件,心血来潮,于是就写了这篇文章。
简介
CSRF (跨站请求伪造),或称之为XSRF, 是一种网络安全漏洞,黑客借用(不一定是盗用)受害者在已经登录过的网站上存留的会话凭证,作为通行证,借用受害者的身份实施的攻击行为。
CSRF特点:属于攻击方式隐蔽,非硬扛目标服务器。身份伪造,危害性大。难以排查,因为请求都是合法请求。
与XSS区别
标题 | XSS | CSRF |
---|---|---|
极简概括 | 项目被植入恶意客户端代码,被受害者客户端执行的行为。 | 利用客户端会话冒充用户身份做坏事的行为。 |
对登录凭证的影响 | 植入恶意代码盗取会话凭证 | 利用会话凭证冒充真实用户 |
注意:
- 如果cookie设置了HttpOnly,XSS将无法获取cookie。
- XSS能比CSRF做更多的攻击行为,但与CSRF不存在包含关系,而是并列关系,两者攻击方式可以结合。
举例
场景一:常规手段
小明登录了安全性不高的网站A,小明的浏览器自然就记录了cookie,黑客也熟悉这个网站A,知道有个更改登录密码的接口:/api/change_pwd?new_pwd=xxx,于是黑客做了一个网站B,小明点进去网站B后,此时小明的密码已被重置为12345678。
场景二:CSRF联合XSS
小明登录了安全性不高的网站A,小明的浏览器自然就记录了cookie,黑客也熟悉这个网站A,知道有个更改登录密码的接口:/api/change_pwd?new_pwd=xxx,也知道这个项目有存储型XSS漏洞,但是网站A的cookie设置了HttpOnly属性,导致恶意代码无法获取cookie,好在可以利用存储型XSS伪造一个提交按钮或者具有诱导性图片,小明点击了这个按钮或图片,此时小明的密码已被重置为12345678。
原理解析
诱导小明点击网站B(这里不纠结是社工引导 ),又或者是网站A伪造的图片或按钮,背后都有一个触发/api/change_pwd?new_pwd=12345678的链接,触发后,请求了网站A的重置密码接口,浏览器自然而然的带上了登录的cookie,很顺利的更改了登录密码。
防御
维护层面(不保险)
网上一些开源项目被很多用户使用,这些项目无法保证一定没有CSRF漏洞,搞不好就是群体性网络安全事件,及时更新补丁,及时排查。
弃用get(不保险)
可以使用POST,避免一个链接就引发的CSRF攻击。
弃用cookie改用token(不保险)
只要浏览器存有cookie,哪怕请求是一个图片也会被带上cookie信息。改用LocalStorage存token后,默认任何请求不会携带token,利用js仅让请求业务接口时带上token就好。
检测referer(不保险)
referer是浏览器请求头的一个属性,用于检测当前请求的资源,是来源于那个页面,如果没有这个属性,那就是本页面。
服务端可以在业务代码前,检测referer的值,如果不是受信任的站点,直接拒绝。注意referer可以伪造。
添加验证码(还行)
关键的节点可以添加验证码的前置验证,通过蹦出来的验证码阻断恶意请求。
如果每个请求都加,用户体感很不好。
添加csrf_token(保险)
前提要保证,有CSRF风险的接口,不允许GET请求,除了GET请求之外的全部需要CSRF_TOKEN,从而实现安全隔离。
- MVC架构(非GET请求下):生成一个复杂的随机字符串作为csrf_token,把这个token存入新的session中,并让页面表单的隐藏域填充csrf_token的值(csrf_token和session中的token是一致的),当表单提交时,服务端需要优先验证表单的csrf_token与session中存的是否一致,如果不存在,失败掉这次请求,如果对不上,也失败掉这次请求。
当黑客实施csrf攻击时,黑客猜不到token,就能防止csrf(但遇到XSS可能会直接窃取表单中的csrf_token,只能说见招拆招,不断完善)。 - 前后端分离架构:直接用token。
为什么我认为get请求不需要csrf_token?
get是用来获取数据的,一般可以定义为检索数据,没有写操作。get也可以添加csrf_token,技术上能实现,但是可能增加复杂度,并且大部分业务场景下不需要。
为什么csrf_token要放置在session中,而不放置在cookie中?
- 若直接将csrf_token明文放入cookie:然后表单提交时同步提交隐藏域的csrf_token和cookie,然后服务端作对比,很明显自欺欺人。
- 若服务端直接将csrf_token不可逆加密后放入cookie:然后表单提交时同步提交csrf_token和cookie,由于没有和服务端强绑定,即使验证是在服务端,但张三的令牌和李四的令牌都能互相使用,还是不够安全。如果是开源项目,或黑客破解了简单的csrf加密算法,那么此时可以依据破解出来的算法任意伪造token。
所以必须要求与服务端强绑定。
前后端分离的架构,用cookie存令牌,还需要csrf_token吗?
没必要,因为有了也不安全。因为用cookie存令牌,csrf_token也无法像MVC那样,在页面加载时同步生成,需要请求接口,期间并不安全(下文有讲)。
为什么前后端分离的架构,不推荐用csrf_token?
- 鉴权会使用存入LocalStorage/SessionStorage的登录凭证(access token)而不是cookie。黑客实施csrf攻击,默认情况下,请求不会自动带上LocalStorage/SessionStorage的登录凭证,也就无法伪造身份。
- 如果强制使用csrf_token,页面加载时请求接口获取csrf_token,此时还未登录,服务器无法认定客户端是谁,就随意下发token,那么黑客可以直接请求接口去获取,甚至造成接口滥用现象,完全没必要。