在CORS中深度潜水:历史,它是如何运作的,以及最好的做法

2021-04-14 05:23:40

从Origin的'https://example.com'获取获取http:// localhost:3000'has被CORS策略阻止。

我相信你在浏览器的商具中看到了这些错误或变体之一。如果你没有 - 不要烦恼,你很快就会。所有开发人员都有足够的Corserrors。

这些在开发期间弹出可能会令人讨厌。但实际上,CORS在一个错误配置的Web服务器的世界中是一种不可思议的有用机制,Web的HostileActors在Web和组织推动前方的网络标准。

子源是一个HTML元素,被请求嵌入到Thedocument中,或在其上下文中执行。在10093年,第一个子源< img>介绍了。通过引入< img>,Web Gotterier。更复杂。

如果您的浏览器将呈现具有< img&gt的页面,则会看到。就此而言,它必须从原点上获取该子资源。当浏览器从不驻留在SameScheme,完全限定的主机名或端口上的原点中的原点时,浏览器源 - 这是一个跨原点请求。

原点是通过三重:方案,完全限定的主机名和端口标识。例如,

你得到了这个想法 - 如果三联中的三个项目中的任何一个不同,那么原产地是不同的。

作为练习,如果我们运行Https://blog.example.com/posts/foo.html原点的比较,则会获取以下结果:

跨原点请求意味着例如,资源(即页面),例如http://example.com/posts/bar.html,它会尝试从https://example.com原点呈现子资源(注意计划变化!)。

现在我们定义了同样和交叉起源的是,让我们看看什么是大片。

当我们介绍< img>到了网络上,我们打开了闸门。不久theweb后得到<脚本>中<帧>中<视频>中<音频>中< iframe中>中<连结>中<形式>等等。这些子资源可以通过浏览器进行后载页面获取,因此它们都可以是相同或跨起源的请求。

让我们前往一个想象中的世界,在那里CORS不存在,Web浏览器都有各种各样的跨起源请求。

想象一下我在我的网站上有一个页面evil.com与< script&gt ;.在Securfacitit上看起来像一个简单的页面,你读过一些有用的信息。但是在<脚本和gt;,我有专门制作的代码,可以将专制的request发送到银行的删除/帐户端点。加载页面后,将执行JavaScript,Ajax调用击中Bank的API。

展示 - 想象一下,在阅读网页上阅读一些信息时,您将从您的银行获得Anebail,您已成功删除您的帐户。我知道iknow& MLDR,如果这很容易与银行做任何事情。我骨折。

对于我的邪恶< script>为了工作,作为浏览器的请求的一部分,也将从银行网站发送您的凭据(cookie)。这就是银行的服务器如何识别您并知道要删除的帐户。

我想检测到为Awesome Corp工作的人,其中artra.awesome-corp.com上的内部网站。在我的网站上,anderation.com我得到了一个< img src =" https://intra.awesome-corp.com/avatars/john-doe.png"&gt ;.

对于没有与intra.awesome-corp.com有效的会话的用户,Theavatar不会呈现 - 它将产生错误。但是,如果您已登录了Awesome Corp的Intranet,请在您打开危险的网站时,我会知道您可以访问。

这意味着我将能够派生一些关于您的信息。虽然我对我来说令人攻击的攻击性难以实现令人敬畏的公司仍然是一个潜在的攻击向量。

虽然这两个是过于简单简单的例子,但这是这种威胁,哈姆制造了同样的政策& CORS Neccessary。这些都是跨原因请求的含义。有些人被减轻了,其他人不能抽泣 - 他们根植于网络的性质。但对于已经被压扁的熟悉钓鱼载体 - 它是因为CORS。

通过阻止从不同的原点加载的读取访问ToreSources来阻止跨原点攻击。此策略仍然允许一些标签,例如< img>,以将资源嵌入不同的原点。

1995年Netscape Navigator 2.02引入了同类策略,最初旨在保护对DOM的交叉来源。

即使同名策略实现不需要遵循ANExACT规范,所有现代浏览器也实现了某种形式。该策略的主题在互联网工程任务(IETF)的RFC6454中描述。

同性政策解决了许多挑战,但它非常严格。在单页应用和媒体沉重的网站中,同样的原产地表明了很多房间放松或微调这些规则。

CORS出生,目标是放宽同样的政策和细乡村原产地访问。

到目前为止,我们涵盖了什么原点,如何定义,浏览器实现的缺点是源的缺点和同类策略。

现在是时候熟悉交叉来源资源共享(CORS)了。 CORS是一种机制,允许在网络上控制AWEB页面上的子资料。该机制对三种不同类别的Subresource Access进行分类:

在我们继续解释这些类别中的每个类别之前,这是重要的是,虽然您的浏览器(默认)可能允许某种类型的跨原点请求,但这并不意味着服务器将被服务器逐渐被抑制。

交叉原点写入是链接,重定向和表单提交。在您的浏览器中使用CoRactive,这些都允许。还有一个被称为预检要求的东西,即微调横向源写道,但默认情况下,虽然默认情况下允许某些手段,但这并不意味着他们可以在实践中进行。我们将稍后调查一下。

跨源嵌入功能是通过加载子资源:其中脚本>中<连结>中< IMG>中<视频>中<音频>中<对象>,<嵌入>中< IFRAME>而且默认情况下都是允许的。 < iframe>是一个特殊的 - 因为它是真正的是在框架内部加载不同的页面,可以通过使用X帧 - 选项标题来控制ITSCross原点帧。

当谈到< img>而另一个嵌入的子资源 - 它在他们的状态下触发交叉原点请求。这就是为什么在CORS中的跨起源嵌入和跨起源读取和跨起作用的原因。

跨起源读数是通过Ajax / fetch调用加载的子元库。默认情况下,此默认情况下浏览器。页面中的嵌入式子资源中有嵌入式子资源,但此类技巧由现代浏览器中的另一个保单支持处理。

如果您的浏览器最新,则所有这些启发式都已经实现了它。

交叉源写入可能是非常有问题的。让我们看一下一个例子安静的CORS。

要求" kemal" port = env [" port"]。to_i || 4000" /"做"你好世界!" EndGet" /问候"做"嘿!" Endpost" /问候" do | env | name = env.params.json ["姓名"]。作为(字符串)"你好,#{name}!" endkemal.config.port = portkemal.run.

它只是在/问候路径中获取请求,在请求人员中具有名称,并返回Hello#{name}!要运行此Tiny Crystal Server,我们可以使用以下内容:

这将引导服务器并侦听localhost:4000。如果我们在我们的浏览器中导航到LocalHost:4000,我们将呈现一个简单的“Hello World”页面:

既然我们知道我们的服务器正在运行,让我们从我们浏览器页面的控制台执行localhost:4000的帖子/迎接theserver侦听。 Wecan使用fetch执行此操作:

获取(' http:// localhost:4000 / hell' {method:'帖子',标题:{' content-type&#39 ;:''应用程序/ json'},body:json.stringify({name:' ilija'})。然后(resp => resp.text())。然后(console.log)

这是一个帖子请求,但它不是跨起源。我们在浏览器中发送了Hoteling,其中http:// localhost:4000(原点)被渲染,tothat tothat同名。

现在,让我们尝试同样的请求,而是横发。我们将打开https://google.com并尝试从yourbrowser中的选项卡发送相同的请求:

我们设法获得了着名的CORS错误。 Athough我们的水晶服务器可以富裕请求,我们的浏览器正在保护我们。它基本上有一个我们开业的网站想要改变另一个recewebsite。

在第一个示例中,我们将请求发送到http:// lockhost:4000 / hell从呈现http:// localhost:4000,我们的浏览器查看该请求并允许它吞吐量,因为我们的网站呼叫似乎是吞吐量我们的服务器(这很好)。丁丁我们的网站(https://google.com)想要对writeto http:// localhost:4000,然后我们的浏览器标志请求并通过它完成。

如果我们在我们的开发人员控制台中深入了解,特别是在网络标签中,我们实际上会注意到两个请求代替我们发送的请求:

有趣的是要注意的是,第一个请求具有HTTP方法的选项,而第二个请求的帖子。

如果我们探索选项请求,我们将看到这是我们在发送我们的POST请求之前由我们的浏览器发送的请求:

有趣的是,即使对选项Requestwas的响应http 200,它仍然标记为请求列表中的红色。为什么?

这是现代浏览器所做的预检要求。对CORS认为复杂的请求执行的预检要求。复杂请求的标准是:

因此,在上面的示例中,虽然我们发送了一个帖子请求,但是由于内容类型:应用程序/ jsonheader,我们的请求复杂。

如果我们会更改服务器来处理文本/普通内容(而不是JSON),我们可以在需要预检请求时解决:

要求" kemal" get" /"做"你好世界!" EndGet" /问候"做"嘿!" Endpost" /问候" do | env | body = env.request.body名字="那里" name = body.gets.as(String)如果!Body.nil? "你好,#{name}!" endkemal.config.port = 4000kemal.run.

获取(' http:// localhost:4000 / held' {method:' post',标题:{' content-type&#39 ;:'文本/ plain'},身体:' ilija'})。然后(resp => resp.text())。然后(console.log)

现在,虽然不会发送预检请求,但是Browser的CORS策略将继续阻止:

但是因为我们制作了一个没有对复杂分类的请求,yorsbrowser实际上不会阻止请求:

简单地说:我们的服务器错误配置以接受文本/普通的横发启发重新查询,而无需任何其他保护,我们的浏览器就无法执行多种。但仍然,它确实是下一个最好的事情 - 它不会将Oupopened页面/标签暴露给该请求的响应。因此,在这种情况下,CORSDOES不会阻止请求 - 它阻止响应。

我们的浏览器的CORS策略有效地考虑了一个横向读,因为虽然请求被发送为帖子,但内容类型标题估计它将其作为GET相同。并且横向读被阻止ByDefault,因此我们在“网络”选项卡中看到的封锁响应。

不建议在上面的示例中的预检请求上工作。事实上,如果您希望您的服务器必须优雅地处理预检请求,则应实现选项端点并返回正确的标题。

在实现选项端点时,您需要知道浏览器的前高度最重要的是,特别是在响应上表现出三个标题:

访问控制允许方法 - 它表示响应的URL支持哪些方法,以便CORS协议的目的。

访问控制允许标头 - 它表示响应的URL支持哪些标题,以便CORS协议的目的。

访问控制 - 最大年龄 - 它表示可以缓存访问控制 - 允许 - 方法和访问控制允许标题的信息的秒数(5)。

让我们回到我们发送复杂请求的前面的示例:

获取(' http:// localhost:4000 / hell' {method:'帖子',标题:{' content-type&#39 ;:''应用程序/ json'},body:json.stringify({name:' ilija'})。然后(resp => resp.text())。然后(console.log)

我们已经确认了,当我们发送此请求时,我们的浏览器将在服务器检查服务器,如果它可以执行跨原点请求。要在横向环境中获取此请求,我们必须先将选项/ readent端点添加到服务器。在其响应标题中,新终端将通知浏览器将返回/ read的请求与Content-type:application / json header,来自原点https://www.google.com,可以接受。

选择" /问候" do | env | #允许`post / heth` ... env.response.headers ["访问控制 - 允许 - 方法"] ="帖子" #... Content-type`标题中的请求... env.Response.Headers ["访问控制 - 允许标题和#34;] ="内容类型" #...来自https://www.google.com来源。 env.Response.Headers ["访问-Control-allial- origin"] =" https://www.google.com"结尾

我们的请求仍然被阻止。即使我们的选项/ readendpoint删除了请求,我们仍然看到错误消息。在我们的网络中,Tabhere正在进行有趣的事情:

对选项/迎接端点的请求成功!但帖子/招呼呼叫仍然失败。如果我们在帖子/问候请求内部偷看,我们将看到一个熟悉的视觉:

实际上,请求确实成功 - 服务器返回了HTTP 200.PREFLIGHT请求确实有效 - 浏览器确实制作了POST请求而不是博锁。但是,POST请求的响应不包含任何Corshegers,因此即使浏览器确实提出请求,它也会阻止任何响应处理。

要允许浏览器还处理响应的帖子/问候请求,我们还需要将CORS头部添加到后端点:

通过添加访问控制允许原点标头响应标题,我们告诉browser具有https://www.google.com打开的选项卡也可以访问therishonse payload。

我们会看到帖子/迎接确实给我们一个回复,没有任何错误。如果我们在网络选项卡中拍摄,我们会看到两个请求都是绿色的:

通过在我们的预选端点选项/ ready上使用正确的响应标题,WeunLocked我们的服务器的帖子/迎接端点以跨越不同in。最重要的是,通过在帖子/迎接端点的附近提供正确的CORS响应标题,我们将浏览器释放到不带任何阻塞的情况下处理。

如前所述,默认情况下,横向读取被阻止。这是onpurpose - 我们不希望从我们的原籍国的曲折中加载其他折扣中的其他资源。

从带有www.google.com的标签呈现,如果我们尝试获取Get / read Endpoint,我们将被CORS阻止:

事实上,就像以前一样,我们的浏览器确实让请求通过 - 我们得到了AHTTP 200后面。但它没有将打开的页面/标签暴露在答案的响应中。同样,在这种情况下,CORS不会阻止请求 - 它阻止响应。

就像用横向写作一样,我们可以放宽CORS并使它可以通过添加访问-Control-allize-Origin标头来实现ofcross-origin reading -

当浏览器从服务器获取响应时,它将查看访问控制允许原点标题,如果ITCAN允许该页面读取响应,则将根据其值决定。鉴于这种情况下的值是https://www.google.com,这是我们在我们的示例中使用的页面,TheOutcome将成功:

这就是浏览器如何屏蔽我们的跨起源读取并尊重通过标题发送的TheServer指令。

正如我们在前面的示例中看到的那样,要放宽CORSWBSITE的CORS策略,我们可以设置我们/迎接动作的访问控制 - 允许https://www.google.com值:

帖子" /问候" do | env | body = env.request.body名字="那里" name = body.gets.as(String)如果!Body.nil? env.Response.Headers ["访问-Control-allial- origin"] =" https://www.google.com" "你好,#{name}!"结尾

这将允许https://www.google.com来源调用我们的服务器,而且Ourbrowser会对此感觉很好。具有访问控制允许原点的inplace,我们可以尝试再次执行获取呼叫:

这使它成为工作!通过新的CORS策略,我们可以通过HTTPS://www.google.com的选项卡调用/ readyMactFormation。或者,Wecould也将标题值设置为*,这将告诉浏览器可以从任何原点调用Theserver。

必须仔细考虑这种配置。然而,放松的Corshegers几乎总是安全的。一个拇指规则是:如果打开URL InnInito选项卡,并且您对您所曝光的信息感到满意,则您可以在所述URL上设置允许(*)CORS策略。

我们网站上的微调CORS的另一种方法是使用Access-Control-ally-enderentials响应标题。访问控制 - 允许凭据指示浏览器在请求的Credetials模式包括时向前端JavaScript代码泄露。

该请求的凭据模式来自引入FetchApi,其根源返回原始的XMLHTTPRequest对象:

随着提出的引入,HOTCredentials选项将转换为fetch调用的可选参数:

凭据凭据选项的可用选项省略,同样的原点包含。不同的模式可用,因此开发人员可以进行微调出站请求,而来自服务器的响应会通知Browser如何在凭据与请求发送凭据时(通过访问控制 - 允许凭据标题)。

fetch API规范包含CORS和FETCH Web API的相互作用的良好写入和全面的细分,以及浏览器普及的安全机制。

在我们将其包装之前,让我们在涉及横梁资源共享(CORS)时涵盖一些最佳实践。

一个常见的例子是,如果您拥有一个向公众显示内容的网站,那不是PayWalls的内容,或需要身份验证或授权 - 您可以将访问控制 - 允许原点设置:*到其资源。

起源&将访问资源的客户具有伟大品种,您无法了解它,或者您根本不在乎

这种配置的危险前景是涉及内容伺服专用网络(即防火墙或VPN)。通过AVPN连接时,您可以访问公司网络上的文件:

现在,如果攻击者主机作为网站anderation.com,其中包含VPN中的链接TOA文件,它们可以(理论上)在其网站上创建一个脚本可以访问该文件: 虽然这种攻击很难,但需要大量了解VPN和存储在其中的文件,这是一个潜在的攻击载体,我们必须beabware。 从上面继续举例,想象我们希望为我们的网站实施Analytics。 我们 ......