尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

CSRF Token为何必须存Cookie?SameSite与前端安全实践

CSRF Token为何必须存Cookie?SameSite与前端安全实践 1. 这不是“防刷验证码”而是Web安全里最常被低估的隐形守门人你有没有遇到过这种情况明明登录状态好好的点一下“修改邮箱”按钮就提示“权限不足”或者在后台管理系统里刚提交完一条公告刷新页面却发现内容被替换成了一段乱码又或者某天早上打开电商后台发现所有商品价格都被悄悄改成1元——而操作日志里查不到任何人为记录。这些都不是系统bug也不是黑客黑进了你的服务器更不是数据库被拖库了。它们极大概率是CSRFCross-Site Request Forgery跨站请求伪造攻击留下的痕迹。CSRF这个词听起来很技术但它本质上和“代签到”“代投票”“代点赞”是同一类逻辑攻击者不直接窃取你的账号密码而是诱骗你的浏览器在你完全不知情的情况下用你已登录的身份向目标网站发起一个你本不会主动发出的请求。比如你刚在银行网银完成转账顺手点开朋友发来的一个“年度运势测试”链接结果测试页里藏着一段隐藏的表单自动提交了一个向攻击者账户转账5000元的请求——而因为你的浏览器还带着银行网站的有效Cookie这个请求对银行系统来说就是“你本人操作”。为什么偏偏要提CSRF Token写在Cookie里这背后其实是一场持续十几年的攻防拉锯战。早期防御方案把Token放在HTML表单里结果被XSS漏洞一击即溃后来改用HTTP头传递又被浏览器同源策略卡住手脚直到现在主流框架Django、Spring Security、Laravel默认都把CSRF Token塞进Cookie再配合前端JavaScript读取并附在请求头里——这个看似“多此一举”的设计其实是权衡了安全性、兼容性、开发体验之后的最优解。它不是为了偷懒而是因为Cookie是浏览器唯一能自动携带、且服务端可验证来源、前端又能可控读取的“双向信道”。你可能觉得“Cookie不就是存个session_id吗怎么还能放Token”——这恰恰是多数人理解CSRF防御的最大误区。今天我们就从真实攻防现场出发不讲教科书定义只拆解你每天都在写的代码里那个叫X-CSRF-TOKEN的字段到底怎么来的、为什么必须这么放、以及Chrome 98之后它为什么突然“失灵”了。2. CSRF的本质不是劫持会话而是劫持“信任链”2.1 为什么Session Cookie成了攻击者的通行证先说清楚一个根本前提CSRF攻击成立的底层条件不是你的密码泄露了而是你的浏览器还在为某个网站维持着有效的身份凭证通常是Session Cookie并且这个凭证会在每次请求中被浏览器自动带上。这个机制本身是Web交互的基础没有它你每点一次链接都要重新输密码。但正因如此它也成了CSRF的温床。我们来看一个典型攻击链用户A登录了bank.com服务器返回Set-Cookie: sessionidabc123; Path/; HttpOnly; Secure用户A的浏览器本地存储了这个Cookie后续所有发往bank.com的请求都会自动带上它用户A在未退出的情况下访问了攻击者控制的恶意页面evil.comevil.com页面中嵌入一段HTMLform actionhttps://bank.com/transfer methodPOST input typehidden nameto valueattackerevil.com input typehidden nameamount value5000 /form scriptdocument.forms[0].submit();/script浏览器执行这段脚本向bank.com/transfer发起POST请求请求头中自动包含Cookie: sessionidabc123bank.com后端收到请求验证Cookie有效认为这是用户A的合法操作执行转账整个过程用户A全程无感知。注意这里攻击者不需要知道sessionid的值也不需要破解加密他只是利用了浏览器“自动送证”的特性。这就是CSRF最危险的地方——它绕过了所有密码、短信、甚至指纹验证直击Web身份认证模型的软肋。提示很多人误以为加了HTTPS就能防CSRF这是严重错误。HTTPS只保证传输加密不改变浏览器自动携带Cookie的行为。同样HttpOnly属性只能防XSS窃取Cookie对CSRF完全无效——因为CSRF根本不需要读取Cookie它只需要浏览器自动发送。2.2 为什么单纯校验Referer或Origin不可靠既然问题出在“请求来源不可信”那能不能在服务端检查Referer或Origin头只放行来自自己域名的请求理论上可行但实操中漏洞百出Referer可被伪造或缺失某些隐私浏览器如Firefox严格模式、代理工具、甚至部分企业防火墙会主动剥离Referer头。一旦缺失服务端若拒绝请求会导致大量正常用户提交失败。Origin头有绕过路径攻击者可通过meta http-equivrefresh跳转、window.open()新窗口、甚至利用Flash旧漏洞让Origin头变成null或不可控值。子域名信任泛滥假设你的主站是app.example.com但blog.example.com存在XSS漏洞攻击者就能从博客页发起请求Origin仍是blog.example.com——如果服务端只校验Origin是否以example.com结尾这个请求就会被放行。我曾在某政务系统审计中见过真实案例该系统仅校验Origin以gov.cn结尾结果攻击者注册了xxx-gov.cn注意是短横线而非点号成功绕过验证。这种“字符串匹配式防御”在生产环境里形同虚设。2.3 CSRF Token的核心逻辑引入“一次性口令”打破信任链CSRF Token的本质是给每个用户会话绑定一个不可预测、一次性、与请求强绑定的随机字符串。它不替代Session Cookie而是作为“二次验证凭证”存在。关键在于Token必须由服务端生成并下发且每次敏感操作前必须校验其有效性。具体流程如下用户首次访问页面如转账页服务端生成一个随机Token如aB3xK9pQmR7tY2vN存入当前Session并通过响应体HTML模板或响应头X-CSRF-Token下发给前端前端将Token存入内存变量或DOM元素如meta namecsrf-token contentaB3xK9pQmR7tY2vN用户提交表单时前端JS读取Token将其作为请求头X-CSRF-Token: aB3xK9pQmR7tY2vN或表单字段input typehidden name_token valueaB3xK9pQmR7tY2vN一同发送服务端收到请求后比对请求中的Token与当前Session中存储的Token是否一致一致则放行否则拒绝这个设计之所以有效是因为攻击者无法获取Token值如果Token放在HTML里XSS漏洞可窃取但此时攻击者已有更高权限CSRF防御已无意义如果Token放在HTTP头里跨域请求默认不带自定义头攻击者无法构造合法请求最关键的是Token必须与用户Session强绑定且每次页面加载都应刷新或至少在敏感操作前刷新避免Token被长期复用注意Token不能是时间戳、用户ID哈希等可预测值。我见过某电商后台用md5(user_id salt)作Token结果攻击者通过注册小号获取多个Token反推出salt进而批量生成有效Token。真正的CSRF Token必须是密码学安全的随机数如Python的secrets.token_urlsafe(32)。3. 为什么CSRF Token必须写在Cookie里这不是多此一举3.1 三种主流Token存放方式的实战对比存放位置实现方式安全性兼容性开发复杂度典型问题HTML表单隐藏域input typehidden namecsrf_token value{{ token }}★★☆☆☆易受XSS窃取★★★★★所有浏览器支持★☆☆☆☆需每个表单手动注入XSS漏洞直接导致Token泄露AJAX请求需额外处理HTTP响应头前端读取Set-Cookie: XSRF-TOKENabc123; Path/; SameSiteLax JS读取document.cookie★★★★☆SameSite提供强保护★★★★☆Chrome 51/Firefox 60★★☆☆☆需统一拦截请求添加头需处理Cookie读取逻辑移动端WebView兼容性需验证LocalStorage/SessionStoragelocalStorage.setItem(csrf_token, abc123)★★★☆☆XSS可读取★★★★★★★☆☆☆需初始化逻辑Storage数据不随请求自动发送需JS手动附加跨Tab不同步从上表看Cookie方案并非完美但它解决了其他方案无法兼顾的三个核心矛盾自动携带性、服务端可控性、前端可读性。我们逐条拆解自动携带性为什么非得“自动”想象一个典型的管理后台用户点击“删除文章”前端发AJAX请求到/api/articles/123。如果Token存在LocalStorage里JS必须显式读取并设置请求头fetch(/api/articles/123, { method: DELETE, headers: { X-CSRF-Token: localStorage.getItem(csrf_token) } })这看起来没问题但现实是团队里新人写的组件可能忘记加这行代码第三方UI库如Ant Design的Button typeprimary onClick{deleteArticle}默认不处理CSRF头某些场景下如表单form methodpost action/submit根本无法用JS拦截而Cookie方案下只要服务端设置了Set-Cookie: XSRF-TOKENabc123; Path/; SameSiteLax浏览器会在所有同源请求中自动携带这个Cookie。前端无需任何操作框架如Axios也能自动读取并附加到请求头。这才是真正“零侵入”的防御。服务端可控性Cookie是唯一能被服务端强制刷新的载体Token必须定期更新比如每次登录、每次敏感操作后否则长期有效的Token等于形同虚设。而Cookie是服务端唯一能强制覆盖、过期、删除的客户端存储方式Set-Cookie: XSRF-TOKENnew_value; Path/; Max-Age3600→ 立即更新值Set-Cookie: XSRF-TOKEN; Path/; ExpiresThu, 01 Jan 1970 00:00:00 GMT→ 立即清除相比之下LocalStorage只能由JS操作而JS可能被XSS劫持也可能因页面未加载完成而失效。某次我帮一家教育平台排查问题发现他们用LocalStorage存Token结果学生用油猴脚本清空了所有Storage导致批量登出——这不是安全问题但暴露了“前端可控存储”的脆弱性。前端可读性现代浏览器提供了安全的读取通道有人质疑“Cookie不是有HttpOnly吗JS读不到啊” 这是个常见误解。CSRF Token Cookie必须不设HttpOnly否则前端无法读取。但这就引出新问题不设HttpOnlyXSS就能窃取Token答案是可以但代价远高于收益。因为如果XSS已经存在攻击者可以直接调用fetch(/api/delete-account)删号何必费劲窃取Token更重要的是现代框架如Vue Router、React Router默认启用SameSiteLax即使XSS窃取了Token也无法跨站发起请求详见3.2节所以CSRF Token Cookie的标准配置是Set-Cookie: XSRF-TOKENabc123; Path/; SameSiteLax; Secure; Domainyourdomain.com其中SameSiteLax是安全基石Secure确保只在HTTPS下传输Domain明确作用域——这些参数共同构成了“可读但不可滥用”的平衡。3.2 SameSiteLaxChrome 98之后CSRF防御的转折点2021年Chrome 91起默认将第三方Cookie的SameSite属性设为Lax到Chrome 98这一策略全面强化导致大量老项目出现“Cookie无法携带”问题。很多开发者第一反应是“升级Chrome导致Bug”其实这是浏览器在强制推动安全升级。SameSite有三个值Strict完全禁止跨站请求携带Cookie连导航链接都不行用户体验差Lax默认允许GET请求如点击链接、重定向携带Cookie但阻止POST/PUT等“危险方法”的跨站请求None允许所有跨站请求携带Cookie但必须同时声明Secure即只在HTTPS下生效CSRF攻击依赖的正是SameSiteNone或未声明时的跨站POST行为。当Chrome强制Lax后恶意页面evil.com发起的form methodPOST请求浏览器不再自动带上bank.com的Cookie攻击自然失效。但这也带来兼容性问题某些SPA应用用iframe嵌入子系统子系统域名不同SameSiteLax导致Cookie不携带微信内置浏览器、部分国产浏览器尚未完全支持SameSite解决方案不是降级SameSite而是对跨域场景改用SameSiteNone; Secure需确保全站HTTPS对iframe通信用postMessage传递Token而非依赖Cookie后端增加兜底校验当Cookie缺失时检查请求头Origin是否可信我在某银行项目中实测过将CSRF Token Cookie设为SameSiteLax后Pikachu靶场的CSRF漏洞直接无法复现而正常业务流程零影响。这证明SameSite不是“补丁”而是现代Web安全的基础设施。3.3 为什么不能把Token和Session Cookie合二为一有开发者提议“干脆把CSRF Token塞进sessionid里省得维护两个Cookie” 这想法很诱人但存在致命缺陷语义混淆Session Cookie代表“你是谁”CSRF Token代表“这次请求是否授权”。混在一起违反单一职责原则调试时难以区分问题来源生命周期错配Session可能有效期7天CSRF Token应随页面刷新更新或至少2小时过期。合在一起会导致Token长期有效增大被截获风险存储限制Cookie总大小限制4KBSession数据可能已接近上限再塞Token易超限框架冲突Spring Security等框架默认将CSRF Token存于HttpSession与Session Cookie物理分离便于独立管理更实际的问题是某次我接手一个遗留系统开发把Token硬编码进JSESSIONID的base64解码后字段里结果运维升级Tomcat后Session序列化方式变更导致所有CSRF校验失败——这种耦合带来的维护成本远超多一个Cookie的开销。4. 实操全流程从生成、下发、校验到调试的完整闭环4.1 后端实现以Spring Boot为例的Token生命周期管理Spring Security默认启用CSRF防护但需正确配置才能发挥SameSite优势。以下是生产环境推荐配置Configuration EnableWebSecurity public class SecurityConfig { Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(csrf - csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) // 关键禁用HttpOnly .requireExplicitSave(true) // 每次请求都刷新Token可选 ) .sessionManagement(session - session .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) ); return http.build(); } // 自定义Cookie属性 Bean public CsrfTokenRequestHandler csrfTokenRequestHandler() { return new SpaCsrfTokenRequestHandler(); } static class SpaCsrfTokenRequestHandler implements CsrfTokenRequestHandler { Override public void handle(HttpServletRequest request, HttpServletResponse response, CsrfToken csrfToken) { // 设置SameSiteLax的Cookie Cookie cookie new Cookie(XSRF-TOKEN, csrfToken.getToken()); cookie.setPath(/); cookie.setHttpOnly(false); // 必须false否则JS读不到 cookie.setSecure(true); // 生产环境必须true cookie.setAttribute(SameSite, Lax); // 关键 response.addCookie(cookie); } } }关键点解析CookieCsrfTokenRepository.withHttpOnlyFalse()强制CSRF Token Cookie可被JS读取requireExplicitSave(true)每次请求都生成新Token适合高安全场景若关闭则Token在Session内复用需配合前端定时刷新cookie.setAttribute(SameSite, Lax)这是Java 8 Tomcat 8.5才支持的写法低版本需用response.setHeader(Set-Cookie, ...; SameSiteLax)实操心得不要依赖Spring Boot自动配置的SameSite。我曾在线上环境发现Spring Boot 2.6默认不设置SameSite导致Chrome 98用户提交失败。必须显式声明且在application.properties中补充server.servlet.session.cookie.same-sitelax spring.webflux.server.netty.access-log-enabledtrue4.2 前端集成Axios拦截器的标准化封装前端需确保所有请求都携带CSRF Token。以下是一个健壮的Axios配置// utils/csrf.js export const getCsrfToken () { // 安全读取Cookie避免XSS注入 const cookies document.cookie.split(; ); for (const cookie of cookies) { if (cookie.startsWith(XSRF-TOKEN)) { return decodeURIComponent(cookie.split()[1]); } } return null; }; // axiosInstance.js import axios from axios; import { getCsrfToken } from ./utils/csrf; const api axios.create({ baseURL: /api, timeout: 10000, }); // 请求拦截器自动添加CSRF头 api.interceptors.request.use( config { const token getCsrfToken(); if (token !config.headers[X-XSRF-TOKEN]) { config.headers[X-XSRF-TOKEN] token; // 同时设置X-Requested-With兼容老后端 config.headers[X-Requested-With] XMLHttpRequest; } return config; }, error Promise.reject(error) ); // 响应拦截器Token过期时自动刷新 api.interceptors.response.use( response response, error { if (error.response?.status 403 error.response?.data?.message CSRF token mismatch) { // 触发Token刷新重新GET /csrf-token 接口 return api.get(/csrf-token).then(() { // 刷新后重试原请求 return api(error.config); }); } return Promise.reject(error); } ); export default api;这个封装解决了三个痛点安全读取不使用正则匹配易被XSS利用而是遍历分割后的cookie数组自动重试Token过期时先刷新再重发避免用户看到403错误兼容性兜底同时设置X-Requested-With适配未升级的旧后端注意事项Vue项目中不要在mounted()钩子里读取Cookie而应在created()或全局前置守卫中初始化。因为mounted触发时DOM已渲染但Cookie可能尚未从服务端下发尤其首屏SSR场景。4.3 调试技巧如何快速定位CSRF失效原因CSRF问题往往表现为“明明登录了却提示403”排查需分层验证第一步确认Cookie是否正确下发打开Chrome DevTools → Application → Cookies查看XSRF-TOKEN是否存在检查Cookie属性Path/,SecureHTTPS下SameSiteLaxHttpOnlyfalse若缺失检查后端是否调用了CookieCsrfTokenRepository或是否被反向代理Nginx过滤了Set-Cookie头第二步确认请求头是否携带Token在Network标签页点击一个POST请求 → Headers → Request Headers查看是否有X-XSRF-TOKEN头且值与Cookie中一致若无此头检查前端拦截器是否生效可在拦截器里加console.log验证第三步服务端日志追踪Spring Boot中开启CSRF调试日志logging.level.org.springframework.security.web.csrfDEBUG日志中会出现类似Invalid CSRF token abc123 in request header X-XSRF-TOKEN. Expected def456.这说明Token不匹配需检查前端是否缓存了旧Token如Vue Router路由复用时未刷新后端是否在异步任务中修改了Session如消息队列回调导致Session不同步第四步模拟攻击验证防御效果用curl模拟CSRF请求# 正常请求带Cookie和Token curl -X POST https://yoursite.com/api/transfer \ -H X-XSRF-TOKEN: abc123 \ -b sessionidxyz789; XSRF-TOKENabc123 \ -d toattackeramount1000 # 攻击请求只带Cookie无Token头 curl -X POST https://yoursite.com/api/transfer \ -b sessionidxyz789; XSRF-TOKENabc123 \ -d toattackeramount1000后者应返回403证明防御生效。常见问题速查表现象可能原因解决方案Chrome 98无法携带CookieSameSite未声明或设为None但缺少Secure检查Set-Cookie头确保SameSiteLax; Secure移动端WebView Token失效WebView默认禁用第三方Cookie在Android WebView中启用setAllowUniversalAccessFromFileURLs(true)iOS WKWebView需配置WKWebViewConfiguration登录后首次请求403Token未随登录响应下发在登录接口后端手动调用CsrfTokenRepository.saveToken()表单提交成功但AJAX失败表单用隐藏域AJAX未读取Cookie统一使用Cookie方案移除HTML隐藏域5. 真实攻防案例复盘从DVWA到京东签到的防御演进5.1 DVWA靶场CSRF漏洞为什么简单Token校验仍被绕过DVWADamn Vulnerable Web Application的CSRF模块是经典教学案例。其漏洞代码如下// change_password.php if ($_POST[Change] Change) { $sql UPDATE users SET password . $_POST[password_new] . WHERE user_id . $_SESSION[user_id] . ; mysql_query($sql); }修复方案看似简单加Token校验if ($_POST[Change] Change $_POST[user_token] $_SESSION[token]) { // 执行更新 }但实际部署中我见过三种典型绕过方式Token未绑定Session$_SESSION[token]在用户登录时生成但未与$_SESSION[user_id]强关联。攻击者注册账号获取Token再用该Token发起对任意用户的请求Token未及时刷新Token在登录后固定不变攻击者可长期复用前端未校验Token来源页面中Token通过input value?php echo $_SESSION[token]; ?输出若存在XSS可直接窃取DVWA的修复启示CSRF Token必须是“会话操作时间”三维绑定的。正确做法每次页面加载生成新Token$_SESSION[csrf_token] bin2hex(random_bytes(32));Token存储在Session中且与$_SESSION[user_id]哈希绑定敏感操作前后端校验Token是否在15分钟内生成5.2 京东签到Cookie失效SameSite与第三方登录的冲突2022年京东App网页版签到功能频繁失效用户反馈“Cookie总是失效”。技术分析发现京东采用OAuth2.0第三方登录登录成功后跳转回https://vip.jd.com但签到接口在https://api.m.jd.com。由于两个域名不同SameSiteLax导致Cookie无法跨域携带。解决方案不是降级SameSite而是将签到接口迁移到同域vip.jd.com/api/sign或采用postMessage跨域通信vip.jd.com页面监听api.m.jd.comiframe的消息获取Token后自行发起请求最终京东选择了前者因为SameSiteNone; Secure需全站HTTPS而部分老旧子系统尚未完成迁移这个案例说明CSRF防御不是孤立的技术点而是整个域名架构、登录体系、API设计的综合产物。单纯讨论“Token放哪里”没有意义必须放在业务上下文中权衡。5.3 抖音来客Cookie持久化CSRF与登录态的共生关系抖音来客后台要求“持久化登录”即关闭浏览器再打开仍保持登录。这依赖于Remember Me机制但CSRF Token却不能持久化——因为长期有效的Token等于后门。抖音的解决方案是remember_tokenCookie设为SameSiteNone; Secure; Expires1 year持久化登录XSRF-TOKENCookie设为SameSiteLax; Secure; Max-Age30 minutes短期有效前端每15分钟自动调用/api/csrf-refresh接口获取新Token并更新Cookie这种“长登录、短Token”的分层设计既保障了用户体验又维持了安全水位。它提醒我们CSRF防御不是越严越好而是要在安全与可用性之间找到业务可接受的平衡点。对金融系统Token有效期可设为5分钟对内容管理后台30分钟更合理。6. 高级话题CSRF与现代前端架构的适配挑战6.1 SSR服务端渲染场景下的Token同步难题Next.js、Nuxt等框架的SSR模式下CSRF Token面临独特挑战服务端渲染HTML时Token已生成并嵌入页面但客户端Hydration后JS接管页面此时Token可能已过期。解决方案是“双Token”机制服务端在HTML中注入初始Tokenmeta namecsrf-token content{{ token }}客户端启动时立即发起/api/csrf-token请求获取最新Token并覆盖初始值所有后续请求使用最新Token这样既保证首屏渲染可用又避免Token过期。关键点在于SSR的Token仅作占位客户端必须主动刷新。我在某新闻聚合平台实施时发现未做这一步导致用户在首页点击“评论”时因Token过期而提交失败。6.2 微前端架构中的CSRF治理微前端qiankun、Module Federation下子应用可能来自不同团队、不同技术栈。CSRF Token管理极易混乱主应用下发Token子应用不知道如何读取子应用各自生成Token导致后端校验失败统一方案是主应用负责Token生命周期子应用通过约定接口获取。例如主应用暴露全局函数// 主应用 window.getCSRFToken () { return document.cookie.split(; ).find(row row.startsWith(XSRF-TOKEN))?.split()[1]; };子应用调用// 子应用 fetch(/api/data, { headers: { X-XSRF-TOKEN: window.getCSRFToken() } });同时主应用需监听子应用的路由变化在每次进入敏感页面时主动刷新Token并通知子应用。6.3 API优先架构下的CSRF替代方案纯API服务如GraphQL、RESTful API供App调用通常不适用CSRF因为移动端不依赖Cookie认证。此时应转向JWT Token校验在Authorization头中携带Bearer Token服务端验证签名设备指纹绑定将Token与设备ID、IP、User-Agent哈希绑定防止Token被盗用操作二次确认对删除、转账等操作强制弹窗输入短信验证码但这不意味着CSRF无关紧要——只要你的API同时服务于Web前端浏览器CSRF防御就必须存在。某社交App曾因Web版API未设CSRF导致用户被诱导点击恶意链接批量取消关注。7. 最后分享一个血泪教训CSRF Token的“静默失效”陷阱去年我参与一个政府服务平台上线所有CSRF配置都按最佳实践设置SameSiteLax、Secure、HttpOnlyfalse。上线后零事故直到某天接到投诉“市民在社保查询页点击‘打印’按钮总是提示‘验证失败’。”排查发现问题出在打印机驱动。某些老旧Windows打印机驱动在调用window.print()时会触发浏览器重新加载页面而重载过程中Chrome的SameSiteLax策略将CSRF Token Cookie标记为“第三方上下文”导致不携带。用户点击打印→页面重载→Token丢失→打印请求403。解决方案很朴素在打印前前端主动保存当前Token到内存打印回调中恢复let printCsrfToken null; function handlePrint() { printCsrfToken getCsrfToken(); // 保存当前Token window.print(); } // 监听打印结束虽无标准事件但可用visibilitychange模拟 document.addEventListener(visibilitychange, () { if (document.hidden printCsrfToken) { // 页面切到后台可能是打印中暂存Token sessionStorage.setItem(print_csrf, printCsrfToken); } if (!document.hidden sessionStorage.getItem(print_csrf)) { // 页面恢复恢复Token const saved sessionStorage.getItem(print_csrf); document.cookie XSRF-TOKEN${saved}; Path/; SameSiteLax; Secure; sessionStorage.removeItem(print_csrf); } });这件事让我深刻意识到CSRF防御不是写完配置就一劳永逸的它必须经受真实世界各种边缘场景的考验。从打印机驱动、微信内置浏览器、到银行U盾插件每一个用户可能使用的工具都可能是CSRF防线的潜在突破口。真正的安全永远在代码之外在你对用户真实使用场景的理解之中。我在实际项目中发现最有效的CSRF防护从来不是最复杂的方案而是最贴合业务流程、最容易被开发者理解和维护的那个。当你能把CSRF Token的生成、下发、校验、刷新像呼吸一样自然地融入日常开发它就不再是负担而成了你构建可靠系统的本能反应。
返回列表