访问安全过滤跨站请求异常
一个正常的访问表单提交,被服务器拦了。安全返回 403,过滤页面空白,跨站用户一脸懵。请求开发者打开日志,异常看到一行字:Cross-Site Request Forgery detected。访问这就是安全跨站请求异常,安全过滤机制在干活,过滤但它有时候干得太过头。跨站
事情往往出在 Token 上。请求CSRF Token 的异常设计逻辑很简单——服务器发一个随机串给前端,前端每次提交请求都带上它,访问服务器验证这个串是安全否匹配。问题在于,过滤Token 的生成、存储、校验三个环节,任何一个出偏差,请求就会被判定为跨站伪造。我见过一个案例:某后台系统用 Redis 存 Token,设置了 30 分钟过期。用户填一个长表单花了 40 分钟,点提交,Token 早没了。服务器一看,没有有效 Token,直接拒绝。用户以为是网络问题,反复提交,反复失败。
还有一种更隐蔽的情况。前端用 JavaScript 动态获取 Token,写入请求头。如果页面加载时 Token 请求失败——比如接口超时、跨域被拦——后续所有提交都会带空 Token。服务端收到空值,按安全策略直接拦截。开发者查了半天后端逻辑,最后发现是前端一个 fetch 没加 catch。
安全过滤的严格程度需要权衡。设得太松,CSRF 攻击确实能得手——攻击者诱导用户点击恶意链接,利用用户已登录的 Cookie 向目标站发请求。设得太紧,正常用户的合法操作被误杀。某电商平台的购物车接口就踩过这个坑:他们给每个商品操作都加了 Token 校验,但商品详情页是静态缓存的,Token 嵌入在 HTML 里,缓存一刷新,Token 就变了。用户加购时用的还是旧 Token,请求全被拒。后来他们改成 Token 只校验会话级别,不绑定单个页面,问题才解决。
调试这类问题,第一步是看请求头。浏览器开发者工具里,检查 Cookie 是否带上、Token 字段是否存在、Referer 是否被篡改。第二步看服务端日志,确认拦截规则触发的是哪一条。第三步做对比测试:用 Postman 直接发请求,绕过前端,看是否仍然被拦。如果 Postman 能过,问题在前端;如果 Postman 也被拦,问题在服务端规则。
有些框架的默认配置本身就容易引发异常。Spring Security 默认开启 CSRF 防护,对 POST、PUT、DELETE 都校验 Token。前后端分离的项目里,前端如果没正确配置 Token 的发送方式,后端就会一直返回 403。解决办法是在 Security 配置里对特定接口放行,或者改用 SameSite Cookie 属性来防御 CSRF,减少对 Token 的依赖。
SameSite 属性是个好东西。把 Cookie 设为 SameSite=Lax 或 Strict,浏览器在跨站请求时就不会带上这个 Cookie。攻击者从第三方站点发起的请求,天然缺了认证信息,CSRF 就失效了。但 SameSite 也有局限:老浏览器不支持,跨域单点登录场景下可能影响正常跳转。所以很多系统还是 Token 和 SameSite 一起用,双保险。
回到那个 403 页面。用户看到的只是一个错误,背后是安全策略、前端实现、网络环境三者的博弈。开发者要做的,不是把安全过滤关掉,而是找到那个平衡点——让合法请求通过,让伪造请求现形。这活儿不轻松,但总比被拖库强。
