为什么设置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: * 的问题在于:它让任何网站都能跨域请求你的 API,并且能读取响应内容。浏览器虽然不会自动带上 cookie(除非你额外配置了 withCredentials),但如果你的接口本身不依赖 cookie,或者用了 token、session ID 在 header 里,那攻击者完全可以伪造请求,把敏感数据拿走。你日志里看到的攻击者请求,就是靠这个通配符绕过同源策略直接读取了响应体,哪怕没 cookie 也能偷到敏感信息。
关键点在于:CORS 是服务端配合浏览器的“放行机制”,不是防攻击的盾牌,但它配置错了,真能成突破口。
至于你动态判断 origin 的中间件,OPTIONS 返回 404 的问题,大概率是没处理预检请求。Express 默认不会自动处理 OPTIONS 请求,你得显式加个中间件专门处理它,比如这样:
注意几个细节:
- 一定要返回 204 状态码,别用 200,不然浏览器可能继续等响应体
- Access-Control-Allow-Headers 要包含你实际用到的自定义头,比如 Content-Type、Authorization 什么的
- 用 Vary: Origin 是为了防止 CDN 或代理缓存错误的 CORS 头,尤其在多个域名共用一个后端时很关键
还有一点容易踩坑:如果你用了 cookie 认证,那跨域时前端必须加 credentials: 'include',后端还得加 Access-Control-Allow-Credentials: true,而且这时候 Access-Control-Allow-Origin 就不能再用 *,必须是具体域名,否则浏览器会直接拦截请求——这个配置组合很容易漏掉。
最后说句实话:生产环境别图省事用 *,真要动态 origin,建议用一个专门的 CORS 中间件,比如 cors(npm i cors),它帮你把预检、credentials、origin 校验这些坑都填好了,比自己写稳当多了。
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配置有问题,也能多一层保护。最后提醒一句,调试这种问题的时候,打开浏览器开发者工具的网络面板,仔细观察请求和响应头,能帮你更快定位问题。