为什么设置CORS的’Access-Control-Allow-Origin’为’*’时存在安全风险?

佳怡酱~ 阅读 62

我在开发一个前后端分离的项目,前端用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,是不是还有其他配置没考虑到?

我来解答 赞 6 收藏
二维码
手机扫码查看
2 条解答
博主莉娜
你这个问题问得挺实在的,通配符 * 确实是个“看似方便实则埋雷”的配置,我来拆开说说。

首先,Access-Control-Allow-Origin: * 的问题在于:它让任何网站都能跨域请求你的 API,并且能读取响应内容。浏览器虽然不会自动带上 cookie(除非你额外配置了 withCredentials),但如果你的接口本身不依赖 cookie,或者用了 token、session ID 在 header 里,那攻击者完全可以伪造请求,把敏感数据拿走。你日志里看到的攻击者请求,就是靠这个通配符绕过同源策略直接读取了响应体,哪怕没 cookie 也能偷到敏感信息。

关键点在于:CORS 是服务端配合浏览器的“放行机制”,不是防攻击的盾牌,但它配置错了,真能成突破口。

至于你动态判断 origin 的中间件,OPTIONS 返回 404 的问题,大概率是没处理预检请求。Express 默认不会自动处理 OPTIONS 请求,你得显式加个中间件专门处理它,比如这样:

const allowedOrigins = ['https://safe.site.com', 'https://staging.site.com'];

app.use((req, res, next) => {
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.header('Access-Control-Allow-Origin', origin);
res.header('Vary', 'Origin');
}

// 处理预检请求
if (req.method === 'OPTIONS') {
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.header('Access-Control-Max-Age', '86400'); // 缓存24小时
return res.status(204).end(); // 204 空响应
}

next();
});


注意几个细节:
- 一定要返回 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 校验这些坑都填好了,比自己写稳当多了。
点赞 5
2026-02-26 21:06
技术晓莉
设置 Access-Control-Allow-Origin* 的确是个常见但危险的做法,原因很简单:它允许任何站点跨域访问你的资源。攻击者可以利用这个配置发起跨站请求伪造(CSRF)攻击。比如你提到的用户登录接口返回的cookie被其他站点盗用的问题,就是因为通配符让恶意站点也能合法地发送请求并获取响应。

具体来说,当 Access-Control-Allow-Origin 设置为 * 时,浏览器会认为你的服务允许所有来源的跨域请求。如果攻击者诱导用户访问了他们的站点,同时你的后端又没有对敏感请求做额外防护,那么攻击者就可以利用用户的已登录状态发起恶意请求,甚至读取返回的数据。

至于你提到的动态设置origin的实现,思路是对的,但还有几个点需要注意:

1. 预检请求OPTIONS返回404的问题,通常是因为你的路由处理逻辑没有正确匹配到OPTIONS请求。你需要确保中间件在所有路由之前执行,并且对OPTIONS请求做出明确响应。

2. 动态判断来源时,除了检查 req.headers.origin,还要确保只允许可信的域名。你的代码里已经有一个白名单数组,这很好,但别忘了处理一些边界情况,比如大小写不一致或者恶意构造的子域名。

3. 别忘了设置 Access-Control-Allow-Credentialstrue,如果你的API需要支持带凭证的请求(比如cookies、HTTP认证等)。不过一旦设置了这个头,就不能再把 Access-Control-Allow-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.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Vary', 'Origin');

// 如果需要支持带凭证的请求
res.setHeader('Access-Control-Allow-Credentials', 'true');
}

// 处理预检请求
if (req.method === 'OPTIONS') {
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
return res.status(204).end(); // 预检请求直接返回204
}

next();
});


另外,建议你在生产环境中启用更严格的防护机制,比如结合CSRF token验证和Referer校验。这样即使CORS配置有问题,也能多一层保护。最后提醒一句,调试这种问题的时候,打开浏览器开发者工具的网络面板,仔细观察请求和响应头,能帮你更快定位问题。
点赞 3
2026-02-17 11:14