您不想遇到的 5 大 CORS 问题
什么是 CORS?
不存在访问控制允许来源标头
响应中的访问控制允许来源标头不能是通配符 *
对预检请求的响应未通过访问控制检查
响应中的访问控制允许凭据标头为“ ”,当请求凭据模式为“include”时,该标头必须为“true”
专业提示
我的应用程序仍然在控制台中显示 CORS 问题,但不知道问题出在哪里
摘要✨
什么是 CORS?
CORS 是跨域资源共享 (Cross Origin Resource Sharing)的缩写,它使用额外的 HTTP 标头来指示浏览器允许在一个源上运行的 Web 应用程序访问来自不同源的资源。例如,如果您的前端与后端托管在不同的平台上,那么您需要发出 HTTP 请求才能从后端获取数据,但浏览器默认会阻止这种请求(因为它托管在跨域,而不是同域)。这是我们采取的一项安全措施,旨在保护客户端免受CSRF 攻击。这就是 CORS 概念的由来。
现在,我将带您了解本周让我彻夜难眠的所有 CORS 错误以及如何修复每一个错误。
不存在访问控制允许来源标头
我对 Cors 一无所知,所以我写了个 Express 应用,并在 React 中添加了一个代理,package.json
以便在开发环境中访问后端路由。但是一旦投入生产,我的应用就一直处于加载状态,控制台也出现了这些错误。
由于我无法截取我自己的错误截图,所以路由有所不同,但信息是一样的。虽然我的产品成功了,本地一切正常,但线上却无法正常工作。
它试图告诉我们,我们的源被 CORS 策略阻止了,所以我们无法从后端访问数据。它还说,没有 Access-Control-Allow-Origin
标头,而这个标头是一个 HTTP 标头,用于指示哪些源可以访问我们的数据。我们需要在其上添加前端端点,以便它能够在我们请求时将其所有数据发送给我们。
使固定
您可以将上述 HTTP 标头添加到服务器的响应中,以避免再次出现此类错误。只需将其添加到服务器的根文件中即可轻松完成。
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
next();
});
这*
是一个通配符,允许所有来源(网站)向您的服务器发出请求,并且不会再抛出此类 CORS 错误。
响应中的访问控制允许来源标头不能是通配符 *
那么问题是,如果你在请求中发送了一些像 cookies 这样的凭证,这意味着你有withCredentials: true
(在 axios 中)或credentials: 'include'
(在 fetch 中),那么它会再次阻止请求并出现类似这样的错误。
这是我的实际错误信息,如果不可读,请阅读下文。
The value of the `Access-Control-Allow-Origin` header in the response must not be the wildcard `*` when the request's credentials mode is `include`. Origin is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
这意味着当服务器获得特定凭据(例如来自用户的 cookie)时,它将不会允许来自所有来源的请求,因此我们再次被 CORS 阻止。
使固定
只需添加您的前端 URL 或任何其他您希望访问 API 的网站即可*
。如果您有多个,请随意用逗号分隔。
对预检请求的响应未通过访问控制检查
什么是预检?
发出预检请求是为了检查 CORS 协议是否被理解,以及发送原始请求是否安全。诸如DELETE, PUT
或其他可以修改数据且请求标头不在CORS 安全列表中的请求可以发出此预检请求。它是一个OPTIONS
请求 ,使用三个 HTTP 请求标头:Access-Control-Request-Method
、Access-Control-Request-Headers
,Origin
请参阅这篇MDN 文章。
如果您的后端未启用预检,您将收到此错误消息。
使固定
我们可以轻松解决这个问题,只需在收到请求时,返回Access-Control-Allow-Methods
包含所有允许的 HTTP 方法和响应状态的响应头即可。所以,让我们添加到我们的中间件中。200
OPTIONS
app.use((req, res, next) => {
if (req.method === "OPTIONS") {
res.header("Access-Control-Allow-Methods", "PUT, POST, PATCH, DELETE, GET");
return res.status(200).json({});
}
next();
});
响应中的访问控制允许凭据标头为“ ”,当请求凭据模式为“include”时,该标头必须为“true”
Access Control Allow Credentials
当你的应用发送带有 Cookie 等凭证的请求时,也需要包含此标头,例如withCredentials: true
axios 或credentials: 'include'
fetch 中已包含此标头。如果你没有此标头,并且随请求一起发送了凭证,则会收到此消息。
使固定
您可以将此标题与其他标题一起添加,如上所示。
app.use((req, res, next) => {
res.header("Access-Control-Allow-Credentials", true);
next();
});
专业提示
如果您使用的是 express/connect,那么您有一个现成的Node.js CORS 中间件包,它可以为您便捷地添加标头。您可以使用 来安装它npm install cors
。
正如所说,设置起来非常简单,如果您只需要启用基本的 cors 功能,您只需编写即可。
const cors = require("cors");
app.use(cors());
它也是可配置的,但默认配置是:
{
"origin": "*",
"methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
"preflightContinue": false,
"optionsSuccessStatus": 204
}
因此默认情况下你的:
Access Control Allow Origin
是*
Access Control Allow Methods
是GET,HEAD,PUT,PATCH,POST,DELETE
- 将 CORS 预检响应传递给下一个处理程序,false。
您可以根据应用程序的需求进行配置,以下是可用选项的列表。
这就是我选择为我的应用程序做的事情。
const origin =
process.env.NODE_ENV === "production"
? process.env.FRONTEND_PROD_URL
: process.env.FRONTEND_LOCAL_URL;
// Setting up cors
app.use(
cors({
origin: origin,
preflightContinue: true,
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
credentials: true,
})
);
credentials 键将设置Access-Control-Allow-Credentials
为 true。您也可以按照我们上面讨论的方式,通过添加每个标头来实现相同的效果。
const origin =
process.env.NODE_ENV === "production"
? process.env.FRONTEND_PROD_URL
: process.env.FRONTEND_LOCAL_URL;
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", origin);
res.header("Access-Control-Allow-Credentials", true);
if (req.method === "OPTIONS") {
res.header("Access-Control-Allow-Methods", "PUT, POST, PATCH, DELETE, GET");
return res.status(200).json({});
}
next();
});
我的应用程序仍然在控制台中显示 CORS 问题,但不知道问题出在哪里
我也遇到过这种情况,我主要用的是微软 Edge,测试用的是 Firefox,所以在这两种浏览器上我的应用都运行良好。但我派去检查应用的人却抱怨出现了 CORS 错误。结果发现他们都用的是 Chrome,而我还没测试过。于是我打开 Chrome 看看,它的控制台仍然显示着我们之前修复的第二个 CORS 问题。这是什么鬼?
然后,在网络选项卡上摆弄了一会儿之后,一个小的警告⚠️符号引起了我的注意,悬停时显示:
A cookie associated with a cross-site resource at <url> was set without `SameSite` attribute. It has been blocked, as Chrome now delivers cookies with cross-site requests if they are set with `SameSite=none` and `Secure`.
事实证明,今年早些时候(2020 年 2 月),随着Chrome 80 的发布,它默认拥有一个安全的 Cookie 分类系统,该系统需要SameSite
Cookie 上的一个属性才能被浏览器访问。它有三个值,Lax, Strict, None
您必须根据您想要赋予的自由度来决定您的 Cookie 应该使用哪一个。
在谷歌搜索了大量信息后,出现了这篇来自 heroku 的文章,Chrome 的变化可能会破坏您的应用程序:为 SameSite Cookie 更新做准备,这篇文章解释了我们为什么需要这个以及如何添加这个属性。
所以既然你在这里,我会告诉你我是如何解决这个问题的。
使固定
我使用了express-session包,它是一个简单的会话中间件,用于创建会话并通过connect-mongo插件将其存储在 MongoDB 中。您可以根据应用需求,像 cors 包一样对其进行配置。
因此,我所要做的就是sameSite
在其cookie
设置中添加一个属性,它就能完美地运行。
const session = require("express-session");
const sessionConfig = {
// ... other methods
cookie: {
sameSite: "none",
},
};
if (process.env.NODE_ENV === "production") {
app.set("trust proxy", 1); // trust first proxy
sessionConfig.cookie.secure = true; // serve secure cookies
}
app.use(session(sessionConfig));
我注意到 secure 属性只能true
在生产环境中使用,这意味着只有使用 HTTPS 的来源才能访问 cookie。而 trust proxy 则表示1
它信任来自前端代理服务器的第一跳。要了解更多信息,请参阅trust-proxy 的文档。
摘要✨
CORS 对于保护用户免受CSRF 攻击非常重要且实用,同样,Google 最新更新的同站点属性策略也很有帮助。虽然连续两天(我就是这样)不断收到这些错误信息可能让人很沮丧,但最终我学到了很多关于如何构建安全服务器和安全身份验证的知识,最终觉得这一切都是值得的。
欢迎查看我构建的身份验证应用项目,我创建它是为了学习使用 Passport 和 Sessions 的本地和 OAuth 策略。源代码可以在我的GitHub上找到。
文章来源:https://dev.to/thebuildguy/top-5-cors-issues-you-don-t-want-to-run-into-16hi