请求头验证 CSRF 为啥还是被拦截了?

长孙子赫 阅读 70

我在前端用 fetch 发请求时加了自定义请求头 X-Requested-With: XMLHttpRequest,后端也配置了校验这个头,但还是被 CSRF 防护拦住了,到底是哪出问题了?

我试过在 Axios 里也加了同样的头,本地开发环境没问题,一上测试环境就 403。后端同事说他们只认这个头,但浏览器发 OPTIONS 预检之后实际请求好像没带上?

fetch('/api/submit', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Requested-With': 'XMLHttpRequest'
  },
  body: JSON.stringify({ data: 'test' })
})
我来解答 赞 5 收藏
二维码
手机扫码查看
2 条解答
南宫恩宇
CSRF 防护一般不只是认一个自定义请求头,你遇到的问题可能和跨域有关。首先确认测试环境的 CORS 配置是否正确。

通用的做法是后端需要设置 Access-Control-Allow-Origin 和 Access-Control-Allow-Headers。特别是后者要包含 X-Requested-With 这个自定义头。

在服务器端配置大概这样:
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, X-Requested-With');


另外注意 OPTIONS 请求是预检请求,浏览器会自动发送,但不会带上你的自定义头。后端要专门处理这个请求返回 200 OK。

还有一个坑就是有些框架的 CSRF 保护默认只对表单提交生效。如果用 token 校验更靠谱些,在请求里加个 X-CSRF-Token 头,值从页面获取。

这问题折腾起来挺烦人,尤其是环境配置不一致的时候。建议先本地抓包看看请求头是不是完整发出去了。
点赞
2026-03-31 19:03
巧玲🍀
问题找到了,就是后端对OPTIONS请求也做了CSRF校验。

浏览器发非简单请求(带自定义头)之前会先发OPTIONS预检,这个预检请求可不会带你的X-Requested-With头。后端如果对OPTIONS也拦截,那就直接跪了。

解决方案很简单:后端在CSRF验证那里加个判断,把OPTIONS请求跳过去。

PHP的话大概长这样:

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
// 预检请求直接放行
http_response_code(200);
exit;
}


或者用中间件的方式,在CSRF验证逻辑的最前面判断请求方法。

还有个更根本的思路:现在主流做法其实是用SameSite Cookie + CSRF Token,单纯靠请求头判断这种方式本身就有漏洞(因为浏览器正常请求确实会带这个头,但攻击者的恶意请求也可以构造这个头)。如果后端还在用这种方案,建议考虑升级一下防护策略。
点赞
2026-03-13 17:04