[漏洞篇]CSRF漏洞详解
[漏洞篇]CSRF漏洞详解
免责声明: 本文主要讲解漏洞原理,以及防御手段,旨在帮助大家更好的了解漏洞危害以及不法分子常见的利用方式,同时帮助大家了解开发中所需要关注的点,切勿拿来做违法事情,否则后果自负。
一、介绍
概念
CSRF(Cross-Site Request Forgery,跨站请求伪造),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF 。它是一种常见的 Web攻击方式,是一种劫持用户在当前已登录的Web应用程序上执行非本意操作一种攻击。
- 说的直白一点就是:别人盗用(劫持)你的身份去做坏事.
(1)别人–>这里指的是攻击者
(2)你---->A站(web服务器)上注册的合法用户(客户端)
(3)做坏事—>去请求合法站点A(比如web服务器)存在CSRF攻击的URL,实现类似发送恶意邮件/转账/创建账户/修改密码等"你不希望执行"操作.
CSRF漏洞与SSRF漏洞区别:
CSRF(Cross-Site Request Forgery 跨站请求伪造):黑客盗用用户身份向服务器发送请求。伪造用户身份向服务器发送请求,并不会攻击到内网服务。
SSRF (Server-Side Request Forgery 服务器端请求伪造) :黑客盗用服务器身份向内网其他服务发送请求。模仿内网服务器直接通过给URL发送攻击代码攻击到服务器所在内网里的机器或服务。
CSRF漏洞与XSS漏洞区别:
CSRF是借用户的权限完成攻击,攻击者并没有拿到用户的权限(不知道用户的cookie等信息,只是借用用户之手发起请求),而XSS是直接盗取到了用户的权限(直接获取到了用户的cookie),然后实施破坏。
危害
- 账户劫持:修改密码、绑定邮箱,导致用户失去账户控制权。
- 资金窃取:触发转账、支付等金融操作。
- 数据篡改:更改用户资料、发布恶意内容(如社交平台发帖)。
- 业务逻辑滥用:利用用户权限执行特权操作(如管理员功能)。
- 组合攻击:与XSS结合扩大攻击面(如窃取Token后绕过CSRF防御)。
示例:用户登录银行网站后访问恶意页面,页面包含一个自动提交的表单,请求银行转账至攻击者账户。
因此一些银行或其他敏感信息app或网页,才会在用户登录账号后,提示用户让用户不要离开本软件进行操作。
原理
漏洞原理图:
1. 漏洞产生原因
(1)http协议使用session在服务端保存用户的个人信息,客户端浏览器用cookie标识用户身份;
(2)cookie的认证只能确保是某个用户发送的请求,但是不能保证这个请求是否是"用户自愿的行为".
(3)这时,用户登录了某个web站点,同时点击了包含CSRF恶意代码的URL,就会触发CSRF
2. 利用条件
(1)用户必须登录A网站,生成了cookie
(2)登录的同时访问了恶意URL(包含CSRF恶意代码的URL).
3. CSRF与XSS关系
CSRF是借用户的权限完成攻击,攻击者并没有拿到用户的权限,而XSS是直接盗取到了用户的权限,然后实施破坏。
(1)XSS主要用户获取用户的cookie信息,达到控制客户端的目的
XSS---->把你的腰牌(用户身份象征也就是cookie)偷到手,黑客自己去搞破坏.
CSRF主要是劫持用户身份,让客户端做一些不愿意做的事.
CSRF---->拿刀劫持你,"借助你的身份"来帮黑客做事.
(2)危害上来说,XSS更大;
(3)从应用难度上来说:CSRF需要满足登录某网站的状态,同时访问了恶意的URL,应用条件比较苛刻。XSS只要一次点击或者存储到服务器即可.
二、实战演示
靶场搭建
# 安装docker(也可通过安装docker desktop软件来安装docker)
apt install docker.io# 通过docker搭建靶场
docker run -d -p 8765:80 8023/pikachu-expect:latest
浏览器输入下面地址,访问靶场页面:http://localhost:8765/vul/csrf/csrf.php
初始化靶场:
实战
Pass 01:CSRF Get
- 访问靶场地址:http://localhost:8765/vul/csrf/csrfget/csrf_get_login.php
- 根据提示进行登录
- 进入用户信息页面修改个人信息,点击submit,并进行抓包(这里我使用Burpsuite演示)
- 根据上面抓包信息,构造修改用户信息URL,然后诱导用户点击。假设这里将用户phonenum改为xx
http://localhost:8765/vul/csrf/csrfget/csrf_get_edit.php?sex=girl&phonenum=xx&add=test&email=test&submit=submit
浏览器访问我们构造的攻击URL,查看页面效果:
因为我们浏览器已经有用户的cookie信息,请求时会自动带上用户的cookie,所以服务器会认为是用户自己主动的修改操作,因此通过GET能直接修改成功。
Pass 02:CSRF Post
渗透神器Burpsuite教程地址
:https://blog.csdn.net/weixin_45565886/article/details/144973331
- 访问靶场地址:http://localhost:8765/vul/csrf/csrfpost/csrf_post.php
- 步骤与上面一致,登录之后然后手动操作,点击修改用户个人信息,同时抓包(这里抓包采用Brupsuite,方便后续构造poc)
- 构造漏洞Poc
点击页面右下角的Test in browser
,复制网址,然后在配置了Burpsuite代理的浏览器中打开:
模拟用户操作(点击恶意网页按钮),查看效果:
成功修改:
这里更隐蔽的话,还可以将表单作为自动提交,即:访问该页面就提交,而并非用户主动点击,实现更无感。
📢注意
:因此大家一定不要随意访问来源不明页面,点击不明按钮。
Pass 03:CSRF Token
- 访问靶场页面:http://localhost:8765/vul/csrf/csrftoken/token_get.php
- 依然是登录之后然后手动操作,点击修改用户个人信息,同时抓包
- 这里我们重新请求,查看token值,发现token值是变化的
- 因此我们直接请求是不行的,需要mock这里的token,破解这里token生成的规则,这里从Burpsuite的插件市场安全csrf token分析插件。
Csrf token原理:每次访问页面的时候,在后端生成一个token然后存放在SESSION中。并且将token渲染到表单中。
然后提交表单的时候,就会携带这个token,后端接收到这个token的时候和session中的token进行对比
如果一致:请求合法
如果不一致:请求失效
解释:
- 银行(服务端)每次办业务时给你一个一次性密码(Token)。
- 你提交请求时必须附带这个密码(否则银行拒绝操作)。
- 骗子即使诱导你点击链接,也无法拿到这个密码,因此攻击失败。
来到Burpsuite插件市场,安装csrf token插件:
插件安装成功后,可以看到Burpsuite顶部菜单栏多了一个CSRF Token Tracker:
5. 配置插件,分析对应网站的token
这里的Host配置IP或域名即可,不用带上端口信息
我本地配置了Host文件,因此下面的插件配置Host为www.pikaku.com
6. 重新抓修改用户请求包,并发送到Repeater重放模块
来到重放模块,修改用户信息,并重新发送请求
点击Send后,重新刷新页面查看效果:
查看CSRF Token Tracker插件信息:
CSRF Token Tracker大致原理:插件通过异步去先一步去获取token的值,然后将请求的token值给替换了。
但是这个地方仅仅做实验应该是可以的,真实场景实用性不强(真实场景的token没有这么好破解)。
三、绕过手段
1. Referer检查绕过
- 利用浏览器隐私设置(禁用Referer)或HTTPS→HTTP降级导致Referer缺失。
- 构造子域名或路径欺骗(如attack.com/?target.com)。
2. CSRF Token破解
- 通过XSS漏洞窃取Token。
- 预测或复用Token(如Token未绑定会话或过期时间过长)。
3. JSONP/CORS滥用
- 利用未正确配置的JSONP接口发送跨域请求。
- 通过CORS信任任意源发起携带Cookie的请求。
4. 同源策略绕过
- 使用+window.open跨域提交。
- 利用Flash插件(已逐渐淘汰)发送请求。
5. HTTP方法篡改
- 将POST请求转换为GET(如通过URL参数传递敏感操作)。
四、防御手段
最佳实践:
- 纵深防御:组合Token、SameSite Cookie、Referer检查等多层防护。
- 敏感操作隔离:关键功能使用独立域名,减少Cookie携带风险。
- 定期审计:检查Token生成、存储逻辑,确保无漏洞。
- 安全教育:提升开发人员对CSRF及关联漏洞(如XSS)的认知。
1. CSRF Token
- 机制:服务器生成随机Token,嵌入表单或请求头,提交时验证。
- 实践:
- Token绑定会话,每次请求更新(防复用)。
- 对敏感操作强制使用POST/PUT/DELETE方法。
详细解释:
- CSRF 攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 cookie 中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的 cookie 来通过安全验证。要抵御 CSRF关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中。
- 因此我们可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。
- 这种方法要比检查 Referer要安全一些,token 可以在用户登陆后产生并放于 session 之中,然后在每次请求时把 token 从 session 中拿出,与请求中的 token 进行比对,但这种方法的难点在于如何把 token 以参数的形式加入请求。
- 对于 GET 请求,token 将附在请求地址之后,这样 URL 就变成 http://url?csrftoken=tokenvalue。 而对于 POST 请求来说,要在 form 的最后加上 ,这样就把 token 以参数的形式加入请求了。
- 但是,在一个网站中,可以接受请求的地方非常多,要对于每一个请求都加上 token 是很麻烦的,并且很容易漏掉,通常使用的方法就是在每次页面加载时,使用 javascript 遍历整个 dom 树,对于 dom 中所有的 a 和 form 标签后加入 token。这样可以解决大部分的请求,但是对于在页面加载之后动态生成的 html 代码,这种方法就没有作用,还需要程序员在编码时手动添加 token。
- 该方法还有一个缺点是难以保证 token 本身的安全。特别是在一些论坛之类支持用户自己发表内容的网站,黑客可以在上面发布自己个人网站的地址。由于系统也会在这个地址后面加上 token,黑客可以在自己的网站上得到这个 token,并马上就可以发动 CSRF 攻击。
- 为了避免这一点,系统可以在添加 token 的时候增加一个判断,如果这个链接是链到自己本站的,就在后面添加 token,如果是通向外网则不加。
不过,即使这个 csrftoken 不以参数的形式附加在请求之中,黑客的网站也同样可以通过 Referer 来得到这个 token 值以发动 CSRF 攻击。这也是一些用户喜欢手动关闭浏览器 Referer 功能的原因。
2. 自定义HTTP头
- 实现:通过前端脚本(如Ajax)添加自定义头(如X-Requested-With)。
- 原理:跨域请求默认无法添加自定义头(受CORS限制)。
详细解释:
- 这种方法也是使用 token 并进行验证,和上一种方法不同的是,这里并不是把 token 以参数的形式置于 HTTP 请求之中,而是把它放到 HTTP 头中自定义的属性里。通过 XMLHttpRequest 这个类,可以一次性给所有该类请求加上 csrftoken 这个 HTTP 头属性,并把 token 值放入其中。
- 这样解决了上种方法在请求中加入 token 的不便,同时,通过XMLHttpRequest 请求的地址不会被记录到浏览器的地址栏,也不用担心 token 会透过 Referer 泄露到其他网站中去。
然而这种方法的局限性非常大,XMLHttpRequest 请求通常用于 Ajax 方法中对于页面局部的异步刷新,并非所有的请求都适合用这个类来发起,而且通过该类请求得到的页面不能被浏览器所记录下,从而进行前进,后退,刷新,收藏等操作,给用户带来不便。
另外,对于没有进行 CSRF 防护的遗留系统来说,要采用这种方法来进行防护,要把所有请求都改为 XMLHttpRequest 请求,这样几乎是要重写整个网站,这代价无疑是不能接受的。
3. SameSite Cookie属性
- 作用:限制Cookie在跨站请求中的发送。SameSite是服务端控制的安全机制,用户侧无法修改。
- Strict:完全禁止跨站携带Cookie(适用于关键操作)。
- Lax:允许安全跨站导航(如GET请求)。
- None:无论什么请求,都携带cookie
详细解释:Chrome 51 开始,浏览器的 Cookie 新增加了一个SameSite属性,用来防止 CSRF 攻击和用户追踪。SameSite Cookie 有三个属性:Lax、Strict 和 None,它们定义了不同的跨站请求行为。
- Strict(严格的):Strict最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。
这个规则过于严格,可能造成非常不好的用户体验。比如,当前网页有一个 GitHub 链接,用户点击跳转就不会带有 GitHub 的 Cookie,跳转过去总是未登陆状态。
- Lax(松懈的):Lax规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。
导航到目标网址的 GET 请求,只包括三种情况:链接,预加载请求,GET 表单。设置了Strict或Lax以后,基本就杜绝了 CSRF 攻击。当然,前提是用户浏览器支持 SameSite 属性
- None:在这种模式下,无论请求是如何发起的,都会携带 Cookie。
Chrome 计划将Lax变为默认设置。这时,网站可以选择显式关闭SameSite属性,将其设为None。不过,前提是必须同时设置Secure属性(Cookie 只能通过 HTTPS 协议发送),否则无效
4. 检查Referer/Origin头
- 验证来源:确保请求来自同一域名或可信源。
- 注意:需处理Referer为空的情况(如HTTPS页面引用HTTP资源)。
详细解释:
- 根据 HTTP 协议,在 HTTP 头中有一个字段叫Referer,它记录了该 HTTP 请求的来源地址。在通常情况下,访问一个安全受限页面的请求来自于同一个网站,比如需要访问 :http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory
- 这时,该转账请求的 Referer 值就会是转账按钮所在的页面的 URL,通常是以 bank.example 域名开头的地址。而如果黑客要对银行网站实施 CSRF 攻击,他只能在他自己的网站构造请求,当用户通过黑客的网站发送请求到银行时,该请求的 Referer 是指向黑客自己的网站。
因此,要防御 CSRF 攻击,银行网站只需要对于每一个转账请求验证其 Referer 值,如果是以 bank.example 开头的域名,则说明该请求是来自银行网站自己的请求,是合法的。如果 Referer 是其他网站的话,则有可能是黑客的 CSRF 攻击,拒绝该请求。
5. 二次确认
- 验证码/密码:关键操作前要求用户交互(如输入验证码、密码)。
- 权衡:安全性与用户体验的平衡。
详细解释:验证码被认为是对抗CSRF攻击最简洁而有效的防御方法。
- CSRF攻击的过程,往往是在用户不知情的情况下构造了网络请求。而验证码,则强制用户必须与应用进行交互,才能完成最终请求。因此在通常情况下,验证码能够很好地遏制CSRF攻击。
- 但是验证码并非万能。很多时候,出于用户体验考虑,网站不能给所有的操作都加上验证码。因此,验证码只能作为防御CSRF的一种辅助手段,而不能作为最主要的解决方案。
6. 框架内置防护
- 工具:Spring Security、Django CSRF中间件等自动处理Token。
- 配置:确保所有状态变更请求启用防护。
7. 提升安全防范意识
- 对普通用户来讲,不要点击来历不明的链接或图片;养成定时退出的好习惯;安装安全防护软件等
参考文章:
https://www.ruanyifeng.com/blog/2019/09/cookie-samesite.html