请求头验证 CSRF 为啥还是被拦截了?
我在前端用 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' })
})
浏览器发非简单请求(带自定义头)之前会先发OPTIONS预检,这个预检请求可不会带你的X-Requested-With头。后端如果对OPTIONS也拦截,那就直接跪了。
解决方案很简单:后端在CSRF验证那里加个判断,把OPTIONS请求跳过去。
PHP的话大概长这样:
或者用中间件的方式,在CSRF验证逻辑的最前面判断请求方法。
还有个更根本的思路:现在主流做法其实是用SameSite Cookie + CSRF Token,单纯靠请求头判断这种方式本身就有漏洞(因为浏览器正常请求确实会带这个头,但攻击者的恶意请求也可以构造这个头)。如果后端还在用这种方案,建议考虑升级一下防护策略。