浏览器API为什么限制跨域请求?

39

XMLHttpRequest需要CORS才能在跨域情况下工作。同样的,网页字体、WebGL纹理和其他一些东西也是这样。总的来说,所有新的API似乎都有这个限制。

为什么?

这很容易被绕过:只需要一个简单的服务器代理即可。换句话说,服务器端代码不被禁止进行跨域请求;为什么客户端代码会被禁止呢?这对任何人提供了什么安全保障吗?

而且这太不一致了:我不能使用XMLHttpRequest,但我可以使用<script src><link rel><img src><iframe>。限制XHR等到底有什么意义呢?

3个回答

62

如果我访问一个恶意网站,我希望确保:

  1. 它无法从我使用的其他网站读取我的个人数据。例如attacker.com读取gmail.com
  2. 它不能代表我在我使用的其他网站上执行操作。例如attacker.com从bank.com转移我的账户资金

同源策略解决了第一个问题。第二个问题称为跨站点请求伪造,目前无法通过现有的跨域限制来解决。

同源策略通常遵循以下规则 -

  • 规则1:不允许您从不同的域读取任何内容
  • 规则2:允许您向不同的域编写任何内容,但规则#1将不允许您读取响应。
  • 规则3:您可以自由地进行跨域GET请求和POST请求,但不能控制HTTP标头

让我们看看您列出的各种事物如何符合上述规则:

  1. <img>标记允许您发出HTTP请求,但除了简单显示它之外,没有办法读取图像的内容。例如,如果我这样做<img src =“http://bank.com/get/latest/funds”/>,请求将通过(规则2)。但攻击者无法查看我的余额(规则1)。

  2. <script>标签与<img>几乎相同。如果您执行类似于<script src =“http://bank.com/get/latest/funds”>的操作,则请求将通过。浏览器还将尝试将响应解析为JavaScript,并失败。

  • 有一个众所周知的 <script> 标签滥用叫做 JSONP,其中您与跨域服务器勾搭起来,以便您可以“读取”跨域。但是,在没有跨域服务器的明确参与下,您无法通过<script> 标签读取响应

  • 样式表的 <link> 主要像 <script> 标签一样工作,除了响应被评估为 CSS。通常情况下,您无法读取响应 - 除非响应某种方式恰好是格式正确的CSS。

  • <iframe> 实质上是一个新的浏览器窗口。您无法读取跨域 iframe 的 HTML。顺便说一下,您可以更改跨域 iframe 的 URL,但是您无法读取 URL。请注意它遵循我上面提到的两条规则。

  • XMLHttpRequest 是进行 HTTP 请求的最通用方法。这完全由开发人员控制;浏览器不会对响应执行任何操作。例如,在 <img><script><link> 的情况下,浏览器假定特定格式,并通常会适当地验证它。但是在 XHR 中,没有规定的响应格式。因此,浏览器强制执行同源策略,并防止您读取响应,除非跨域网站明确允许。

  • 通过 font-face 的字体是异常情况。据我所知,只有 Firefox 需要选择加入行为;其他浏览器让您像使用图像一样使用字体。

  • 简而言之,同源策略是一致的。如果您找到一种方法,可以进行跨域请求并且在没有跨域网站的明确许可下读取响应 - 您将在全球范围内成为头条新闻。

    编辑:为什么我不能通过服务器端代理解决所有这些问题?

    为了让 Gmail 显示个性化数据,它需要来自您浏览器的 cookie。有些站点使用 HTTP 基本身份验证,在其中凭据存储在浏览器中。

    服务器端代理无法访问 cookie 或基本身份验证凭据。因此,即使它可以发出请求,服务器也不会返回特定用户的数据。


    1
    嗯...我开始串联起来了。这绝对是迄今为止最有帮助的答案。但是,你能详细解释一下为什么客户端代码向gmail.com发出GET请求比服务器端代码更糟糕吗?我认为我明白为什么了,但为了让这个回答完整,重要的是要回答“为什么我不能通过服务器端代理来解决所有这些问题”的问题。 - Domenic
    2
    @Domenic - 请看我的更新。当客户端代码发出请求时,cookie会隐式传递。这些cookie允许跨域服务器识别用户,从而返回个性化数据。服务器端代理无法访问这些cookie,因此无法读取个人数据。 - Sripathi Krishnan
    太好了,谢谢!你特别指出了cookie和基本身份验证凭据,这对我非常有帮助;我只想到了cookie。 - Domenic
    我认为这里忽略了跨域脚本标签不会发送当前站点的 cookies,因此攻击者无法冒充你。 - Ruan Mendes
    #2 不正确。旧浏览器可能会在JS中过载对象SET。这就是为什么while(1)的原因。 - Royi Namir
    所以这一切都与cookies有关?确保网站不能使用其他网站存储的cookies来发出请求?那么为什么不只是这一个限制呢?为什么CORS不只是一个“Cookies使用政策”?Access-Control-Allow-Origin应该改变目标站点的cookies还是源站点的cookies被使用。除此之外,所有http都应该像从服务器或本地代码中一样工作。 - Rabbi

    3
    考虑以下情景...
    1. 您访问了我的恶意网站。 2. 我的网站向您的银行网站发起 XHR 请求,并请求银行转账表单。 3. XHR 读取防止 CSRF 的令牌并连同安全令牌一起提交表单,向我的帐户转移一定金额的资金。 4. (我) 获利!
    如果没有同源策略的存在,您仍然可以提交该表单,但无法请求防止 CSRF 的令牌。
    服务器端代码不在客户端计算机上运行。

    3
    实际上不是这样的。同源策略防止从其他网站读取信息,但不会阻止向其他网站写入信息。总的来说,当前浏览器安全策略都无法防止跨站请求伪造攻击(CSRF)。 - Sripathi Krishnan
    @SripathiKrishnan,我更新了我的帖子,希望能更清楚一些。 - alex
    抱歉,我在你的回答中漏掉了“请求令牌”的部分。我以为你的意思是同源策略防止CSRF攻击。 - Sripathi Krishnan
    我不是很明白。您是在描述网络钓鱼攻击,还是已经泄露了用户名/密码的攻击?无论哪种情况,似乎都可以通过服务器端代理轻松完成所有这些工作:为什么在客户端计算机上运行很重要? - Domenic
    @Domenic 不行。你怎么代理依赖于客户端 cookies 的东西呢? - alex

    3
    XHR的主要问题在于它们不仅可以发送请求,而且还能读取响应。几乎可以发送任意请求,但无法读取其响应。这就是为什么原始XHR根本不允许任何跨源请求的原因。
    后来,当需要使用XHR进行跨源请求时,CORS被建立以允许在特定条件下进行跨源请求。其中一个条件是特定请求方法、请求标头字段和包含用户凭据的请求需要所谓的“预检请求”,客户端可以通过该请求检查服务器是否允许该请求。有了这个功能,服务器就有了限制只允许特定来源访问的能力,否则任何来源都可以发送请求。

    我明白CORS的工作原理,但为什么能够读取响应是危险的? - Domenic
    1
    虽然是文档发起请求,但实际上是用户的浏览器发送请求。浏览器会像用户直接请求一样发送任何凭据。响应可能包含敏感信息,只有用户才能访问,但是启动脚本可以访问。这就是为什么原始的XHR不允许任何跨域请求的原因。 - Gumbo
    1
    但是现在的“扩展”XHR(以前称为XHR 2级)在遵守CORS规则时允许跨域请求。 - Gumbo

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