XMLHttpRequest 无法加载 XXX No 'Access-Control-Allow-Origin' 标头
Posted
技术标签:
【中文标题】XMLHttpRequest 无法加载 XXX No \'Access-Control-Allow-Origin\' 标头【英文标题】:XMLHttpRequest cannot load XXX No 'Access-Control-Allow-Origin' headerXMLHttpRequest 无法加载 XXX No 'Access-Control-Allow-Origin' 标头 【发布时间】:2022-01-06 04:33:36 【问题描述】:tl;博士;关于同源政策
我有一个启动 express.js 服务器实例的 Grunt 进程。直到现在,当它开始提供空白页面时,这一切都很好,Chrome(最新版本)的开发人员控制台的错误日志中显示以下内容:
XMLHttpRequest 无法加载 https://www.example.com/ 请求中不存在“Access-Control-Allow-Origin”标头 资源。 Origin 'http://localhost:4300' 因此不允许访问。
是什么阻止我访问该页面?
【问题讨论】:
我正在网站上工作,五分钟前还好。 它是否发布 CORS 标头?也许如果你分享一些代码会更容易看到 似是而非。我应该向哪个部门查询?我只是做骨干。木偶的东西主要是…… 是的。我想部门的组织并不总是统一的,所以这可能是一个模糊的问题,但我想知道我公司的一些后端/路由/系统管理的东西,这似乎是一个熟悉的好借口我自己,所以如果将来有问题我可以提供帮助。 我会在您的操作中询问服务器端的某个人。如果您之前能够访问它,他们一定是对您进行了更改。 【参考方案1】:tl;dr - 答案末尾有一个摘要和标题,以便更容易找到相关部分。建议阅读所有内容,因为它为理解为什么提供了有用的背景,这使得了解如何在不同情况下的应用变得更加容易。
关于同源政策
这是Same Origin Policy。它是浏览器实现的安全功能。
您的特殊情况显示了它是如何为 XMLHttpRequest 实现的(如果您使用 fetch,您将获得相同的结果),但它也适用于其他事物(例如加载到 <canvas>
上的图像或加载的文档变成<iframe>
),只是实现方式略有不同。
(奇怪的是,它也适用于 CSS 字体,但那是因为Foundries 坚持 DRM,而不是同源政策通常涵盖的安全问题)。
可以使用three characters 演示证明需要 SOP 的标准场景:
Alice 是一个拥有网络浏览器的人 Bob 经营一个网站(在您的示例中为https://www.[website].com/
)
Mallory 经营一个网站(在您的示例中为http://localhost:4300
)
Alice 登录到 Bob 的网站,并在那里有一些机密数据。可能是公司内网(只能通过 LAN 上的浏览器访问),或者是她的网上银行(只能通过输入用户名和密码后获得的 cookie 访问)。
Alice 访问了 Mallory 的网站,该网站包含一些 javascript,导致 Alice 的浏览器向 Bob 的网站发出 HTTP 请求(从她的 IP 地址和她的 cookie 等)。这可以像使用XMLHttpRequest
和阅读responseText
一样简单。
浏览器的同源策略阻止 JavaScript 读取 Bob 的网站返回的数据(Bob 和 Alice 不希望 Mallory 访问这些数据)。 (请注意,例如,您可以使用 <img>
元素跨源显示图像,因为图像的内容不会暴露给 JavaScript(或 Mallory)……除非您将画布放入混合中,在这种情况下 会产生同源违规错误)。
为什么在您认为不应该使用同源政策时应用它
对于任何给定的 URL,可能不需要 SOP。出现这种情况的几个常见情况是:
Alice、Bob 和 Mallory 是同一个人。 Bob 提供完全公开的信息...但是浏览器无法知道上述任何一个是否为真,因此信任不是自动的,而是应用了 SOP。在浏览器将其提供给其他网站的数据之前,必须明确授予权限。
为什么同源策略只适用于网页中的 JavaScript
浏览器扩展*
、浏览器开发工具中的“网络”选项卡和 Postman 等应用程序都是已安装的软件。他们不会将数据从一个网站传递到属于不同网站的 JavaScript只是因为您访问了那个不同的网站。安装软件通常需要更慎重的选择。
没有第三方(马洛里)被视为有风险。
*
浏览器扩展确实需要仔细编写以避免跨域问题。 See the Chrome documentation for example.
为什么不用JS就可以在页面中显示数据
在许多情况下,Mallory 的网站会导致浏览器从第三方获取数据并显示(例如,通过添加 <img>
元素来显示图像)。但是,Mallory 的 JavaScript 无法读取该资源中的数据,只有 Alice 的浏览器和 Bob 的服务器可以做到这一点,所以它仍然是安全的。
CORS
错误消息中提到的Access-Control-Allow-Origin
HTTP response 标头是CORS 标准的一部分,该标准允许Bob 明确授予Mallory 的站点通过Alice 的浏览器访问数据的权限。
一个基本的实现只包括:
Access-Control-Allow-Origin: *
…在响应标头中允许任何网站读取数据。
Access-Control-Allow-Origin: http://example.com/
... 将只允许特定站点访问它,并且 Bob 可以根据 Origin
request 标头动态生成它,以允许多个但不是所有站点访问它。
Bob 如何设置响应标头的具体细节取决于 Bob 的 HTTP 服务器和/或服务器端编程语言。 a collection of guides for various common configurations 可能会有所帮助。
注意:有些请求很复杂,会发送一个preflight OPTIONS 请求,服务器必须先响应该请求,然后浏览器才会发送 JS 想要发出的 GET/POST/PUT/Whatever 请求。仅将 Access-Control-Allow-Origin
添加到特定 URL 的 CORS 实现通常会因此而受阻。
显然,通过 CORS 授予权限是 Bob 只会在以下情况下才会做的事情:
数据不是私有的或 马洛里值得信赖但我不是鲍勃!
Mallory 没有标准机制来添加此标头,因为它必须来自 Bob 的网站,而她无法控制。
如果 Bob 正在运行公共 API,那么可能有一种机制可以打开 CORS(可能通过以某种方式格式化请求,或者在登录 Bob 的站点的 Developer Portal 站点后使用配置选项)。不过,这必须是 Bob 实现的一种机制。 Mallory 可以阅读 Bob 网站上的文档以查看是否有可用的内容,或者她可以与 Bob 交谈并要求他实施 CORS。
提及“预检响应”的错误消息
一些跨域请求是preflighted。
这种情况发生在(粗略地说)您尝试发出跨域请求时:
包括 cookie 等凭据 无法使用常规 html 表单生成(例如,具有无法在表单的enctype
中使用的自定义标题或 Content-Type)。
如果您正确地做需要预检的事情
在这些情况下,此答案的其余部分仍然适用,但您还需要确保服务器可以侦听预检请求(将是 OPTIONS
(而不是 GET
, POST
或任何您尝试发送的内容)并使用正确的 Access-Control-Allow-Origin
标头以及 Access-Control-Allow-Methods
和 Access-Control-Allow-Headers
来响应它,以允许您使用特定的 HTTP 方法或标头。
如果您误触发了预检
有时人们在尝试构建 Ajax 请求时会犯错误,有时这些会触发预检的需要。如果 API 旨在允许跨域请求,但不需要任何需要预检的东西,那么这可能会中断访问。
触发此错误的常见错误包括:
试图将Access-Control-Allow-Origin
和其他CORS 响应标头放在请求中。这些不属于请求,不做任何有用的事情(您可以授予自己权限的权限系统有什么意义?),并且只能出现在响应中。
试图在没有请求正文的 GET 请求上放置 Content-Type: application/json
标头来描述其内容(通常当作者混淆 Content-Type
和 Accept
时)。
在这两种情况下,删除额外的请求标头通常足以避免需要预检(这将解决与支持简单请求但不支持预检请求的 API 通信时的问题)。
不透明的响应
有时您需要发出 HTTP 请求,但不需要读取响应。例如如果您将日志消息发布到服务器进行记录。
如果您使用的是the fetch
API(而不是XMLHttpRequest
),那么您可以将其配置为不尝试使用CORS。
请注意,这不会让您执行任何需要 CORS 执行的操作。您将无法阅读回复。您将无法提出需要预检的请求。
它将让您发出一个简单的请求,看不到响应,并且不会在开发者控制台中填写错误消息。
当您使用fetch
发出请求并且无权查看使用 CORS 的响应时,Chrome 错误消息解释了如何操作:
CORS 策略已阻止从源“
https://example.net
”获取“https://example.com/
”的访问权限:请求的资源上不存在“Access-Control-Allow-Origin
”标头。如果不透明的响应满足您的需求,请将请求的模式设置为“no-cors”以获取禁用 CORS 的资源。
因此:
fetch("http://example.com", mode: "no-cors" );
CORS 的替代品
JSONP
Bob 还可以使用像 JSONP 这样的 hack 来提供数据,这就是人们在 CORS 出现之前进行跨域 Ajax 的方式。
它的工作原理是以 JavaScript 程序的形式呈现数据,该程序将数据注入到 Mallory 的页面中。
它要求 Mallory 信任 Bob 不会提供恶意代码。
注意共同主题:提供数据的站点必须告诉浏览器第三方站点可以访问它发送到浏览器的数据。
由于 JSONP 通过附加 <script>
元素以调用页面中已有函数的 JavaScript 程序的形式加载数据,因此尝试在返回 JSON 的 URL 上使用 JSONP 技术将失败 - 通常与一个 CORB 错误——因为 JSON 不是 JavaScript。
将两个资源移动到一个 Origin
如果运行 JS 的 HTML 文档和被请求的 URL 位于同一来源(共享相同的方案、主机名和端口),则它们的同源策略默认授予权限。不需要 CORS。
代理
Mallory 可以使用服务器端代码来获取数据(然后她可以像往常一样通过 HTTP 将数据从她的服务器传递到 Alice 的浏览器)。
它会:
添加 CORS 标头 将响应转换为 JSONP 与 HTML 文档同源该服务器端代码可以由第三方(例如 CORS Anywhere)编写和托管。请注意这对隐私的影响:第三方可以监控谁在他们的服务器上代理了什么。
Bob 不需要为此授予任何权限。
这里没有安全隐患,因为这只是在 Mallory 和 Bob 之间。 Bob 无法认为 Mallory 就是 Alice,也无法向 Mallory 提供 Alice 和 Bob 之间应该保密的数据。
因此,Mallory 只能使用这种技术来读取公共数据。
但请注意,从其他人的网站获取内容并自行展示可能会违反版权,并可能会面临法律诉讼。
编写网络应用程序以外的东西
如“为什么同源策略仅适用于网页中的 JavaScript”一节中所述,您可以通过不在网页中编写 JavaScript 来避免 SOP。
这并不意味着您不能继续使用 JavaScript 和 HTML,但您可以使用其他一些机制来分发它,例如 Node-WebKit 或 PhoneGap。
浏览器扩展
在应用同源策略之前,浏览器扩展可能会在响应中注入 CORS 标头。
这些对开发很有用,但对生产站点不实用(要求您站点的每个用户安装禁用浏览器安全功能的浏览器扩展程序是不合理的)。
它们也倾向于只处理简单的请求(在处理预检 OPTIONS 请求时失败)。
拥有适当的开发环境和本地开发服务器 通常是更好的方法。
其他安全风险
请注意,SOP / CORS 不能缓解需要独立处理的XSS、CSRF 或SQL Injection 攻击。
总结
您无法在您的客户端代码中执行任何操作来启用 CORS 访问某人其他人的服务器。 如果您控制正在向其发出请求的服务器:向其添加 CORS 权限。 如果您对控制它的人友好:让他们为其添加 CORS 权限。 如果是公共服务: 阅读他们的 API 文档,了解他们对使用客户端 JavaScript 访问它的看法: 他们可能会告诉您使用特定的网址 他们可能支持 JSONP 它们可能根本不支持从客户端代码进行跨域访问(这可能是出于安全考虑而深思熟虑的决定,尤其是在您必须在每个请求中传递个性化 API 密钥的情况下)。 确保您没有触发不需要的预检请求。 API 可能会授予简单请求的权限,但不会授予预检请求的权限。 如果以上都不适用:让浏览器改为与 您的 服务器通信,然后让您的服务器从其他服务器获取数据并将其传递。 (还有第三方托管服务将 CORS 标头附加到您可以使用的可公开访问的资源上)。【讨论】:
如果我在本地 LAN 上运行一个 Web 服务器并尝试从 IP/URL 进行 ajax 加载,那会起作用吗?我还没有尝试过。因为我的 web 服务器返回 json 数据将是一个 MCU @Ciastopiekarz — 适用正常的相同来源/不同来源规则。正常的网络路由规则适用。 我读过的最完整的答案,而不仅仅是关于 cors 的链接.. @Quentin - 哇! +1!所以我要理解的是,如果 Alice 使用 CORS 扩展,服务器认为她的 http 调用不是来自 javascript,而是来自浏览器扩展,并将其视为普通的同源请求?跨度> @sn-pkid — 不。在通常情况下,服务器将在响应中发送 CORS 标头,而不关心请求来自何处。浏览器有责任根据响应上的 CORS 标头允许或拒绝对 JS 数据的访问。 (当涉及到预检请求时,服务器上的事情会变得/有点/更复杂)【参考方案2】:目标服务器必须允许跨域请求。为了让它通过 express,只需处理 http 选项请求:
app.options('/url...', function(req, res, next)
res.header('Access-Control-Allow-Origin', "*");
res.header('Access-Control-Allow-Methods', 'POST');
res.header("Access-Control-Allow-Headers", "accept, content-type");
res.header("Access-Control-Max-Age", "1728000");
return res.sendStatus(200);
);
【讨论】:
【参考方案3】:因为在接受的答案中没有提到这一点。
此问题并非如此,但可能会帮助其他搜索该问题的人 这是您可以在客户端代码中执行的操作,以防止在某些情况下出现 CORS 错误。您可以使用Simple Requests。
为了执行“简单请求”,请求需要满足几个条件。例如。只允许POST
、GET
和HEAD
方法,以及只允许一些给定的Headers(你可以找到所有条件here)。
如果您的客户端代码未在请求中使用固定值显式设置受影响的标头(例如“接受”),可能会发生某些客户端确实使用某些“非标准”自动设置这些标头" 值导致服务器不接受它作为简单请求 - 这会给你一个 CORS 错误。
【讨论】:
【参考方案4】:这是由于 CORS 错误而发生的。 CORS 代表跨域资源共享。简而言之,当我们尝试从另一个域访问域/资源时会发生此错误。
在此处了解更多信息:CORS error with jquery
要解决此问题,如果您有权访问其他域,则必须在服务器中允许 Access-Control-Allow-Origin。这可以添加到标题中。您可以为所有请求/域或特定域启用此功能。
How to get a cross-origin resource sharing (CORS) post request working
这些链接可能会有所帮助
【讨论】:
【参考方案5】:没有进一步阐述这个 CORS 问题(出于其他原因)。
我目前遇到这个问题的原因不同。 我的前端也返回“Access-Control-Allow-Origin”标头错误。
只是我指向了错误的 URL,所以这个标头没有正确反映(我一直认为它确实如此)。 localhost(前端)-> 调用非安全http(应该是https),确保前端的API端点指向正确的协议。
【讨论】:
【参考方案6】:我在 Chrome 控制台中遇到了同样的错误。
我的问题是,我试图使用http://
而不是https://
访问该站点。所以没有什么可修复的,只需要使用https
访问同一个站点。
【讨论】:
【参考方案7】:带有附加标头的“获取”请求转换为“选项”请求。因此出现了 Cors 政策问题。您必须向您的服务器执行“选项”请求。
【讨论】:
【参考方案8】:这个错误花了我 2 天的时间。我检查了我的服务器日志,浏览器 Chrome/Edge 和服务器之间的 Preflight Option 请求/响应正常。主要原因是 XHTMLRequest 的 GET/POST/PUT/DELETE 服务器响应 还必须具有以下标头:
access-control-allow-origin: origin
“origin”在请求标头中(浏览器会为您添加它到请求中)。例如:
Origin: http://localhost:4221
您可以添加如下响应标头以接受所有人:
access-control-allow-origin: *
或特定请求的响应标头,例如:
access-control-allow-origin: http://localhost:4221
浏览器中的消息看不懂:"...The requested resource"
请注意: CORS 适用于本地主机。不同的端口意味着不同的域。 如果您收到错误消息,请检查服务器端的 CORS 配置。
【讨论】:
【参考方案9】:在大多数住房服务中,只需在目标服务器文件夹上的 .htaccess 中添加以下内容:
标头集 Access-Control-Allow-Origin 'https://your.site.folder'
【讨论】:
【参考方案10】:我遇到了同样的问题。在我的情况下,我通过在我的 URL 中添加 timestamp
的附加参数来修复它。即使是我正在访问的服务器也不需要这样做。
例如 yoururl.com/yourdocument?timestamp=1234567
注意:我使用的是 epos 时间戳
【讨论】:
【参考方案11】:您应该启用 CORS 以使其正常工作。
【讨论】:
以上是关于XMLHttpRequest 无法加载 XXX No 'Access-Control-Allow-Origin' 标头的主要内容,如果未能解决你的问题,请参考以下文章
XMLHttpRequest 无法加载 XXX No 'Access-Control-Allow-Origin' 标头