同源策略和跨域资源共享(CORS)

58

我正在尝试理解CORS。根据我的理解,它是浏览器实现的一种安全机制,用于避免除用户打开的域之外的任何AJAX请求(指定在URL中)。

由于这种限制,许多CORS被实现以使网站能够进行跨域请求。但根据我的理解,实现CORS违背了“同源策略”(SOP)的安全目的。

CORS仅提供额外的控制,以决定服务器要服务哪些请求。也许它可以避免垃圾邮件发送者。

来自维基百科

为了发起跨源请求,浏览器会发送带有Origin HTTP头的请求。该头的值是服务页面的站点。例如,假设一个页面在http://www.social-network.example上尝试访问online-personal-calendar.example中的用户数据。如果用户的浏览器实现了CORS,则将发送以下请求头:

Origin: http://www.social-network.example

如果online-personal-calendar.example允许请求,则会在其响应中发送Access-Control-Allow-Origin头。该头的值指出哪些来源站点是允许的。例如,对上一个请求的响应将包含以下内容:

Access-Control-Allow-Origin: http://www.social-network.example

如果服务器不允许跨源请求,则浏览器将向social-network.example页面提供错误而不是online-personal-calendar.example的响应。

为了允许访问所有页面,服务器可以发送以下响应头:

Access-Control-Allow-Origin: *

然而,在涉及安全的情况下,这可能并不适合使用。

2个回答

141

同源策略

是什么?

同源策略是浏览器间标准化的一种安全措施。这里的“来源”通常指的是“域名”。它可以防止不同来源之间互相交互,以避免跨站请求伪造等攻击。

CSRF攻击是如何工作的?

浏览器允许网站在客户端计算机上存储信息,以cookie的形式。这些cookie有一些附属信息,例如cookie的名称、创建时间、过期时间和设置cookie的人等。一个cookie看起来像这样:

Cookie: cookiename=chocolate;  Domain=.bakery.example; Path=/ [//  ;otherDdata]

这是一个巧克力曲奇饼干,应该可以从http://bakery.example及其所有子域名中访问。

这个cookie可能包含一些敏感数据。在本例中,该数据是... 巧克力。正如您所看到的那样高度敏感。

因此,浏览器会存储此cookie。每当用户向可访问此cookie的域发出请求时,该cookie将被发送到该域的服务器。服务器很开心。

这是一件好事。对于服务器来说,这是一种非常酷的存储和检索客户端信息的方法。

但问题在于,这使得http://malicious-site.example可以将这些cookie发送到http://bakery.example,而用户并不知情!例如,考虑以下场景:

# malicious-site.example/attackpage

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://bakery.example/order/new?deliveryAddress="address of malicious user"');
xhr.send();

如果您访问了恶意网站,且以上代码执行且不存在同源策略,那么恶意用户将代表您下订单并在他自己的地址上收到订单...您可能不喜欢这样。

发生这种情况是因为您的浏览器向 http://bakery.example 发送了您的巧克力 cookie,这使得 http://bakery.example 认为 正在 有意地 请求新订单。 但实际上不是这样。

换句话说,这是一种 CSRF 攻击。 跨站点伪造请求:“跨站点请求伪造”。由于同源策略,它不起作用。

同源策略如何解决此问题?

它阻止 malicious-site.example 向其他域发送请求。 简单。

换句话说,浏览器不会允许任何站点向任何其他站点发出请求。 它将通过这些请求防止不同来源之间的交互,如 AJAX。

但是,来自其他主机的资源加载,例如图片、脚本、样式表、iframe、表单提交等不受此限制。 我们需要使用CSRF Tokens来保护我们的面包店免受恶意站点攻击。

CSRF Tokens

正如所述,即使在不违反同源策略的情况下,恶意站点仍然可以执行此类操作:

<img src='http://bakery.example/order/new?deliveryAddress="address of malicious user"'/>
浏览器将尝试从该URL加载图像,导致向该URL发送所有Cookie的GET请求。为了防止这种情况发生,我们需要一些服务器端保护。
基本上,我们将适当熵的随机唯一令牌附加到用户会话中,在服务器上存储它,并将其与表单一起发送到客户端。当提交表单时,客户端会将该令牌与请求一起发送,服务器验证该令牌是否有效。
现在我们已经完成了这个过程,恶意网站再次发送请求将始终失败,因为没有可行的方式让恶意网站知道用户会话的令牌。
CORS
在必要时,可以通过跨站点请求来规避策略。这被称为CORS(Cross Origin Resource Sharing)。
这通过让“域”告诉浏览器放松限制并允许此类请求来实现。这个“告诉”的事情可以通过传递头文件来完成。例如:
Access-Control-Allow-Origin: //comma separated allowed origins list, or just *

因此,如果http://bakery.example将此标头传递给浏览器,并且创建对http://bakery.example的请求的页面存在于来源列表中,则浏览器将允许请求进行,并连同cookie一起发送。

按照定义来源的规则进行操作1。例如,对于同一域的不同端口,它们不属于同一来源。因此,如果端口不同,浏览器可能会拒绝此请求。像往常一样,我们亲爱的IE是个例外。IE以相同的方式处理所有端口。这是非标准的,没有其他浏览器会以此方式行事。不要依赖此特性


JSONP

带填充的JSON只是绕过同源策略的一种方法,当CORS不可用时使用。这是冒险和不良实践。避免使用此方法。

该技术涉及向另一个服务器发出请求,如下所示:

<script src="http://badbakery.example/jsonpurl?callback=cake"></script>

由于同源策略不会阻止这个请求2,所以此请求的响应将会被加载到页面中。

这个URL最可能会响应JSON内容。但是,仅仅在页面上包含该JSON内容是无法帮助的。这肯定会导致错误。因此,http://badbakery.example接受一个回调参数,并修改JSON数据,将其包装在传递给回调参数的任何内容中。

因此,不要返回,

{ user: "vuln", acc: "B4D455" }

这是无效的JavaScript代码,会抛出错误。如果运行该代码,会返回以下结果:

cake({user: "vuln", acc:"B4D455"});

如果它是有效的JavaScript,它将被执行并根据cake函数存储在某个位置,以便页面上的其余JavaScript可以使用数据。这主要用于API向其他域发送数据。然而,这是一种不好的做法,会有风险,应该严格避免。

为什么JSONP不好?

首先,它非常有限。如果请求失败,你无法处理任何错误(至少不能以合理的方式)。你不能重试请求等操作。

它还要求你在全局作用域中拥有一个cake函数,这不是很好。如果您需要使用不同回调执行多个JSONP请求,可能需要临时函数来解决这个问题,但这仍然是一种hackish方式。

最后,你正在向DOM插入随机的JavaScript代码。如果你不能100%确定远程服务将返回安全的cakes,那么你不能依赖它。


参考资料

1. https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Definition_of_an_origin

2. https://www.w3.org/Security/wiki/Same_Origin_Policy#Details

其他值得阅读的内容

http://scarybeastsecurity.blogspot.dk/2009/12/generic-cross-browser-cross-domain.html

https://www.rfc-editor.org/rfc/rfc3986(抱歉 :p)

https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy

https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)


1
我已经尝试回答所有 通用 的同源策略问题,并欢迎任何建设性的编辑 :) - user3459110
你不需要使用AJAX来执行你的deliveryAddress攻击。一个img标签就可以工作,除非你首先需要获取CSRF保护令牌。此外,JSONP有什么比使用CORS头更危险的地方吗? - Alexander O'Mara
1
@AlexanderO'Mara 没错,我会在答案中添加一个基于CSRF令牌的保护的简要描述。由于我们将随机的JavaScript代码注入页面中,因此JSONP更加危险。如果API调用失败,这可能会变得混乱。一旦请求被发送,就没有太多控制了,不像XMLHttpRequests。感谢您的反馈! - user3459110
23
同源策略并不能阻止你进行跨域请求,它只会阻止你对跨域请求的响应进行读取访问。(参见《同源策略下跨域网络访问》:https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy)换句话说,攻击者仍然可以成功地发动 CSRF 攻击,但是他无法读取攻击的响应。 - user925861
4
@Stradivari 同源策略确实会阻止您发出跨域请求,但仅限于那些不是“简单”(即在使用CORS时不需要预检查的)请求。 因此,PUT或DELETE将被阻止,但GET或POST不会执行(但您无法读取响应)。 - David Klempfner
显示剩余4条评论

11

同源策略(SOP)是浏览器实施的一项政策,旨在通过防止跨站点脚本攻击(XSS)来防止漏洞。这主要是为了保护服务器,因为服务器可能处理身份验证、cookies、会话等情况。

跨域资源共享(CORS)是放宽SOP的少数技术之一。由于默认情况下SOP处于"开启"状态,因此在服务器端设置CORS允许通过XMLHttpRequest发送请求,即使该请求来自不同的域名也可以。如果您的服务器旨在为其他域提供请求(例如,如果您提供API),则这将变得非常有用。

我希望这澄清了SOP和CORS之间的区别以及各自的目的。


1
CORS是放宽SOP的少数技术之一。在我看来,实现浏览器对CORS的支持可能会创建漏洞,并完全绕过SOP提供的安全保护。 - David
CORS实际上是在服务器中实现的。当“启用”时,它只是通过响应发送一个HTTP头(Access Control Allow Origin)。浏览器实现SOP。如果它没有看到正确的头,则会阻止响应。 - compid
2
对我来说,CORS 在错误的服务器上实现了。唯一允许禁用 SOP 的服务器是源服务器,而不是发出 AJAX 请求的第二个服务器。 - Biber
2
我认为SOP的主要目的是防止CSRF攻击,而不是XSS攻击。 - Marcus Junius Brutus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接