为什么跨域Ajax是一个安全问题?

42
为什么决定使用XMLHTTPRequest进行XML调用时不允许跨域请求?可以从其他域检索JavaScript、图像、CSS、iframes以及几乎所有我能想到的其他内容。为什么不允许Ajax HTTP请求跨越域边界?考虑到唯一可能被滥用的情况是有人将Javascript注入页面,这似乎是一个奇怪的限制。然而,在这种情况下,你只需向文档添加img、script或iframe元素来请求第三方URL并将其发送到服务器。
[编辑]
一些答案指出了以下原因,让我们指出它们不会成为禁止此操作的重大原因。
XSRF(跨站点请求伪造,也称为CSRF、XSRF)
您可以完全不使用此操作执行XSRF攻击。一般来说,根本不使用XMLHTTPRequest,因为很难以一种与所有主要浏览器兼容的方式进行XMLHTTPRequest。如果您希望它们加载您的URL,则更容易只需向URL添加一个img标记。
发布到第三方站点
<script type="text/javascript">
  $.post("http://some-bank.com/transfer-money.php", 
         { amount: "10000", to_account: "xxxx" })
</script>

可以通过以下方式实现

<body onload="document.getElementById('InvisbleForm').submit()"
    <div style="display:none">
        <form id="InvisbleForm" action="http://some-bank.com/transfer-money.php" method="POST">
            <input type="hidden" name="amount" value="10000">
            <input type="hidden" name="to_account" value="xxxxx">
        </form>
    </div>
</body>

JPunyon:为什么你要在一个新功能中留下漏洞

你并没有创造更多的不安全因素。你只是让那些想要以正确方式使用它的开发者感到不便。任何想要用这个功能来干坏事(也就是很棒的事情)的人都可以使用其他方法。

结论

我将从bobince的答案标记为正确,因为他指出了关键问题。由于XMLHTTPRequest允许您以凭据(cookie)向目标站点发送数据,并读取从站点发送回的数据,同时发送用户的凭据,您可以编写一些JavaScript来提交一系列表单,包括确认表单和任何随机生成的密钥,以尝试防止XSRF攻击。通过这种方式,您可以浏览目标网站(如银行),而银行的Web服务器无法区分是否仅有普通用户提交了所有这些表单。


修改那些攻击,因为它们实际上是XSRF攻击。当您将脚本注入到合法页面站点上时出现XSS。 - Shawn
@Kibbee,你在吗?我想编辑你的留言以使其更清晰。在某些情况下,我并不完全理解你的英语。你能否审核一下? - Kirill Kobelev
@KirillKobelev 这些编辑看起来不错。很高兴看到这个古老的问题仍然活跃和健康。 - Kibbee
我决定写下自己的答案。你能看一下吗? - Kirill Kobelev
因为XMLHTTPRequest允许您使用凭据(cookies)向目标站点发送POST请求,并读取站点返回的数据。攻击者只需使用例如header('Access-Control-Allow-Origin: *'),就可以读取站点返回的数据并提交表单等操作。或者我有什么误解吗? - Sever
9个回答

37
为什么不允许Ajax HTTP请求跨域边界。
因为AJAX请求(a)使用用户凭据提交,并且(b)允许调用者读取返回的数据。
正是这些因素的结合可能导致漏洞。有提议添加一种省略用户凭据的跨域AJAX形式。
你可以简单地向文档中添加img、script或iframe元素,但这些方法都不允许调用者读取返回的数据。
(除了脚本设置为允许那样的允许跨域脚本 - 或者某人犯了可怕的错误的情况之外。)
您可以完全不使用这个方法就进行XSS攻击。发布到第三方站点。
那不是XSS攻击。那是跨站请求伪造攻击(XSRF)。已知有解决XSRF攻击的方法,例如包括一次性或加密的令牌来验证提交是否来自用户并且没有从攻击者代码启动。
如果允许跨域AJAX,则会失去这种保护措施。攻击代码可以请求银行网站的页面,在其中读取任何授权令牌,并在第二个AJAX请求中提交它们以执行转移。那将是一次跨站脚本攻击。

这是一个很好的回答,但在最后一段中,我仍然认为那不是XSS攻击。 XSS攻击是指将JavaScript注入到合法网站的网页中。 - Shawn
SQL注入确实是获取XSS的最常见方法,但并不是唯一的方法。能够进行AJAX跨域的效果与SQL注入所导致的效果是相同的。 - bobince
SQL注入不是获取XSS的最常见方法,最常见的方法是从服务器发送未编码的用户提交文本。 - Shawn
抱歉,是HTML注入。天啊,有时候我打字思维真的很差。SQL注入是一种更严重的攻击级别... - bobince
我写了自己的答案。你能看一下吗? - Kirill Kobelev

8
POST和GET之间的一个重要区别是:
<body onload="document.getElementById('InvisbleForm').submit()" ...

POST和Ajax之间的区别在于,进行任何POST操作后,浏览器将替换页面,而进行Ajax调用后不会。POST的结果将是:
1. 用户可以清晰地看到。 2. 攻击会停留在此处,因为来自my-bank.com的响应页面将接管控制。没有银行会实现“一键转账”。
如果允许跨域Ajax,则XSRF的场景如下:
1.用户以某种方式访问www.bad-guy.com。
2.如果其他浏览器实例中没有打开到my-bank.com的页面,则攻击失败。
3.但是,如果已经打开这样的页面,并且用户已经输入了用户名/密码,则这意味着浏览器缓存中存在此会话的cookie。
4.来自www.bad-guy.com页面的JavaScript代码发出对my-bank.com的Ajax调用。
5.对于浏览器来说,这是一个常规的HTTP调用,它必须将my-bank cookies发送到my-bank.com并发送它们。
6.银行处理此请求,因为它无法将此次通信与用户的常规活动区分开来。
7.JavaScript代码能够读取响应的事实并不重要。在攻击案例中,这可能是不必要的。真正重要的是,站在计算机前的用户不知道这种交互正在进行。他将在www.bad-guy.com页面上看到漂亮的图片。
8.JavaScript代码如果需要,会对my-bank.com发出多个其他调用。
要点是不需要注入或任何页面篡改。
更好的解决方案可能是允许调用本身,但不发送任何cookie。这是一种非常简单的解决方案,不需要进行任何广泛的开发。在许多情况下,Ajax调用都会去到未受保护的位置,并且不发送cookies不会有限制。
正在讨论的CORS(跨域资源共享)等内容,涉及发送/不发送cookie。

2

如果他们决定我们应该能够进行跨域AJAX调用,如果他们将其作为现有浏览器的更新提供,那将是很好的。 - Kibbee
同意 :) 我讨厌通过本地包装器在网站之间复制功能。 - Andrew Rollings

2
当您向服务器发送HTTP请求时,服务器设置的cookie也会由浏览器发送回服务器。服务器使用这些cookie来建立用户已登录等事实。
恶意攻击者可以利用这一点,借助一些JavaScript,窃取信息或在用户不知情的情况下在其他网站上执行未经授权的命令。
例如,可以要求用户访问一个具有以下JavaScript代码(假设使用jQuery)的站点:
<script type="text/javascript">
  $.post("http://some-bank.com/transfer-money.php", 
         { amount: "10000", to_account: "xxxx" })
</script>

如果用户在执行上述代码时真正登录到银行,攻击者就可以向账户XXX转移10,000美元。这种攻击被称为跨站点请求伪造(XSRF)。有关此问题的更多信息,请参见维基百科。主要是由于这个原因,同源策略存在,浏览器不允许您在与来源不同的域上执行XMLHttpRequests。正在进行一些讨论,以实际允许跨域XHR,但我们必须看看是否真正被接受。

只有在银行特别只查看邮寄的值时,这才是一个问题。在许多情况下,实现网站的人只是查看“请求”,其中包含POST和GET。 - Kibbee
你可以通过让他们加载以下网址 http://some-bank.com/transfer-money.php?amount=10000&to_account=xxxxx 来完成同样的事情。 - Kibbee
此外,您可以在 iframe 中创建一个隐藏表单,并将其操作指向 http://some-bank.com/transfer-money.php,然后通过 Javascript 自动填充并提交。如果银行没有检查引用者,它会认为用户正在进行有效请求。 - Kibbee
引荐者可以被欺骗。您不应该使用引荐者进行身份验证。查找CSRF,您将了解如何防止此类Web漏洞。 - BacMan
这可以使用普通的JavaScript完成。与面向对象编程问题无关。 - Shawn

1

这是一个问题,因为正如你所提到的,它可以被用于恶意目的。但是它也可以被用于良好的意图,因此跨域协议正在开发中。

最大的两个问题是当它与跨站脚本(XSS)和跨站请求伪造(CSRF)一起使用时。这两者都是严重的威胁(这就是为什么它们进入了OWASP前十名和SANS 25)。

我唯一能想到它被滥用的方式,就是如果有人注入Javascript

这就是XSS。太多的应用程序仍然容易受到攻击,如果浏览器安全模型不能防止跨域AJAX,它们会向用户敞开一个相当大的攻击向量。

你只需添加一个img、script或iframe元素到文档中,就可以让它请求第三方URL

是的,但这些将发送HTTP_REFERRER,并且(通过其他手段)可以被阻止以防止CSRF。AJAX调用可以更轻松地欺骗头文件,并允许其他绕过传统CSRF保护的手段。


1

我认为另一个将其与普通XSRF攻击区分开来的因素是,您可以通过JavaScript对返回的数据进行操作。


1

我不知道有什么巨大的问题?将AJAX调用发送到其他域名,首先发送到您的应用程序,然后使用过滤数据转发到其他位置,如果确实需要,解析返回的数据,并将其提供给用户。

处理敏感的AJAX请求?通过检查标头、存储会话时间数据或将传入的IP地址过滤到信任源或应用程序的来源来钉住传入的吸盘。

在未来,我个人希望看到默认情况下所有Web服务器、框架和CMS上的所有传入请求都具有坚不可摧的安全性,然后显式地定义资源,以从外部源解析请求。


1

使用<form>标签,您可以提交数据,但无法读取。使用XHR,您可以同时完成这两个操作。

http://bank.example.com/display_my_password这样的页面对XSRF是安全的(假设它只显示密码而不设置密码)并且对框架也是安全的(它们具有同源策略)。但是跨域XHR将成为一个漏洞。


0

你将毫无戒备的访客变成了拒绝服务攻击者。

此外,想象一下一个跨站脚本,它窃取了你所有的Facebook信息。它打开一个IFrame并导航到Facebook.com

你已经登录了Facebook(cookie),它读取你的数据/好友。然后做更多的恶意行为。


1
但是像这样的攻击已经存在,而且完全不依赖于XMLHTTPRequest。 - Kibbee

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