是否存在允许跨站点AJAX请求的安全方法?


Is there any secure way to allow cross-site AJAX requests?

我目前正在编写一个脚本,网站所有者可以安装,允许用户突出显示一个词,并在一个小的弹出式div中看到该词的定义。我只是在业余时间这样做,无意出售它或任何东西,但我希望它是安全的。

当文本突出显示时,它向我的域发送一个AJAX请求到一个PHP页面,然后该页面在数据库中查找该单词并输出包含该信息的div。我知道同源策略禁止我使用普通AJAX实现这一点,但我也不能使用JSONP,因为我需要返回HTML,而不是JSON。

我考虑过的另一个选项是添加

header("Access-Control-Allow-Origin: *");

到我的PHP页面。

由于我在安全方面没有太多的经验,因为我做这只是一个爱好,有人能给我解释一下使用Access-Control-Allow-Origin: *的安全风险吗?或者有没有更好的办法让我去做?

跨域资源共享(CORS), Access-Control-Allow-Origin报头字段背后的规范,是为了允许通过XMLHttpRequest进行跨域请求而建立的,但通过提供允许服务器定义允许哪些跨域请求和不允许哪些跨域请求的接口来保护用户免受恶意站点读取响应。所以CORS不仅仅是简单的Access-Control-Allow-Origin: *,它表示允许来自任何来源的XHR请求。

现在回答您的问题:假设您的服务是公共的,不需要任何身份验证,使用Access-Control-Allow-Origin: *允许来自任何来源的XHR请求是安全的。但是,请确保只在您希望允许该访问策略的脚本中发送头字段。

"当文本被突出显示时,它向我的域发送一个AJAX请求到一个PHP页面,然后该页面在数据库中查找该单词并输出包含该信息的div。我知道同源策略禁止我使用普通AJAX实现这一点,但我也不能使用JSONP,因为我需要返回HTML,而不是JSON。"

正如hek2mgl所指出的,JSONP可以很好地工作。您所需要做的就是将HTML包装在JSONP包装器中,如下所示:

displayDefinition({"word": "example", "definition": "<div>HTML text...</div>"});

其中displayDefinition()是一个JS函数,显示一个弹出与给定的HTML代码(并可能缓存它供以后使用)。

"我研究的另一个选项是将header("Access-Control-Allow-Origin: *");添加到我的PHP页面。因为我在安全方面没有太多的经验,这只是我的爱好,有人能给我解释一下使用Access-Control-Allow-Origin: *的安全风险吗?"

风险基本上与JSONP相同;在任何一种情况下,你都允许任何网站向你的脚本发出任意的GET请求(他们实际上可以这样做)并读取结果(使用普通的JSON,他们通常不能,尽管旧的浏览器可能有一些安全漏洞允许这种情况)。特别是,如果用户在登录到您的站点时访问了恶意网站,并且如果您的站点可能通过JSONP或CORS暴露敏感用户数据,那么恶意站点就可以获得对这些数据的访问权。

对于您所描述的用例,任何一种方法都应该是安全的,只要您只将其用于特定的脚本,并且只要脚本只执行您描述的操作(查找单词并返回它们的定义)。

当然,对于不希望任何网站访问的脚本(如银行转账表单),您不应该使用CORS或JSONP。这样的脚本,如果它们可以修改服务器上的数据,通常还需要使用额外的防御,如反CSRF令牌,以防止"盲目"CSRF攻击,攻击者并不真正关心响应,而只关心请求的副作用。显然,反csrf令牌本身是敏感的用户特定数据,因此不应该通过CORS, JSONP或任何其他绕过同源保护的方法获得。

"或者我应该寻找更好的方法来做到这一点?"

另一种方法(虽然不一定更好)可能是PHP脚本将定义作为HTML返回,并且弹出窗口仅由指向脚本的iframe元素组成。

JSONP应该满足您的需求。它是一种广泛部署的web技术,旨在解决跨域问题。您还应该了解CORS,它解决了JSONP的一些缺点。我给您的链接还将包含有关这些技术的安全注意事项的信息。

你写的

:

,但我也不能使用JSONP,因为我需要返回HTML,而不是JSON。

为什么不呢?您可以使用这样的JSONP响应:

callback({'content':'<div class="myclass">...</div>'});

,然后使用DOM操作将result.content注入当前页面。

CSRF(跨站点请求伪造)的概念可能是您关心的http://en.wikipedia.org/wiki/Cross-site_request_forgery有多种方法可以限制这个问题,最常用的方法是使用csrf token

此外,您还应该将基于IP的Rate limiter用于"限制执行从某个IP发出的a到number请求",以限制如果您是目标,可以完成的DoS攻击,您可以从如何限制我的网站API用户中寻求一些帮助?

CORS问题很简单-您希望任何人能够远程AJAX您的东西在您的域?如果您的表单容易发生CSRF,那么这可能是非常危险的。这是我直接想到的一个例子。

设置:

  • 网银服务器将CORS标头设置为接受所有(ACAO: *)(称为A)的银行
  • 已登录的合法客户(称为B)
  • 一个恰好能够让客户端运行任何东西的黑客(称之为E)

A<->B对话被视为合法。然而,如果黑客可以设法让标记(B)加载一个网站,用一点JS可以触发AJAX请求(很容易通过永久的XSS缺陷在大网站),他/她可以让B通过JSON向a发送请求,这将被允许并被视为正常请求!

你可以用它做很多可怕的事情。假设银行有一个表单,其中输入如下:

POST:
* targetAccountID -> the account that will receive money
* money -> the amount to be transferred

如果标记已登录,我可以注入:

$.ajax({ url: "http://A/money.transfer.php"; data { targetAccountID: 123; money: 9999; }; });

突然间,任何访问该网站并登录到A的人都会发现他们的帐户耗尽了9999个单位。

这个就是为什么CORS是要带点盐的-在实践中,不要打开超过你需要打开的。打开你的API,就可以了。

一个很酷的边注,CORS不能在IE9之前的任何东西上工作。所以你需要建立一个回退,可能是iframes或JSONP。

我在不久之前写过这个话题:http://www.sebrenauld.co.uk/en/index/view/access-json-apis-remotely-using-javascript,顺便说一下,比维基百科稍微快乐一点。这是一个我非常关心的话题,因为我已经多次与API开发作斗争了。