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 标头附加到您可以使用的可公开访问的资源上)。