为什么设置CORS的’Access-Control-Allow-Origin’为’*’时存在安全风险?
我在开发一个前后端分离的项目,前端用fetch调用另一个域名的API时,后端设置了Access-Control-Allow-Origin: *,虽然能正常跨域请求了,但看到资料说这样有安全风险。试过改成分具体域名后问题依旧存在,但不确定具体哪里出错。
比如用户登录接口返回的cookie被其他站点盗用了,后来查日志发现攻击者用了我的通配符配置发起请求。现在搞不清楚:为什么设置成*就会让攻击者能读取响应数据?如果必须动态设置origin,应该如何正确实现呢?
尝试过在Express中用中间件动态判断来源:
app.use((req, res, next) => {
const allowedOrigins = ['https://safe.site.com', 'https://staging.site.com'];
const origin = req.headers.origin;
if(allowedOrigins.includes(origin)) {
res.header('Access-Control-Allow-Origin', origin);
res.header('Vary', 'Origin');
}
next();
});
但测试时发现预检请求OPTIONS返回404,是不是还有其他配置没考虑到?
Access-Control-Allow-Origin为*的确是个常见但危险的做法,原因很简单:它允许任何站点跨域访问你的资源。攻击者可以利用这个配置发起跨站请求伪造(CSRF)攻击。比如你提到的用户登录接口返回的cookie被其他站点盗用的问题,就是因为通配符让恶意站点也能合法地发送请求并获取响应。具体来说,当
Access-Control-Allow-Origin设置为*时,浏览器会认为你的服务允许所有来源的跨域请求。如果攻击者诱导用户访问了他们的站点,同时你的后端又没有对敏感请求做额外防护,那么攻击者就可以利用用户的已登录状态发起恶意请求,甚至读取返回的数据。至于你提到的动态设置origin的实现,思路是对的,但还有几个点需要注意:
1. 预检请求OPTIONS返回404的问题,通常是因为你的路由处理逻辑没有正确匹配到OPTIONS请求。你需要确保中间件在所有路由之前执行,并且对OPTIONS请求做出明确响应。
2. 动态判断来源时,除了检查
req.headers.origin,还要确保只允许可信的域名。你的代码里已经有一个白名单数组,这很好,但别忘了处理一些边界情况,比如大小写不一致或者恶意构造的子域名。3. 别忘了设置
Access-Control-Allow-Credentials为true,如果你的API需要支持带凭证的请求(比如cookies、HTTP认证等)。不过一旦设置了这个头,就不能再把Access-Control-Allow-Origin设置为*,否则浏览器会直接拒绝。下面是一个改进后的Express中间件示例:
另外,建议你在生产环境中启用更严格的防护机制,比如结合CSRF token验证和Referer校验。这样即使CORS配置有问题,也能多一层保护。最后提醒一句,调试这种问题的时候,打开浏览器开发者工具的网络面板,仔细观察请求和响应头,能帮你更快定位问题。