如何阻止其他网站发送跨域 AJAX 请求?

信息安全 javascript 阿贾克斯
2021-09-07 15:23:39

从两个不同的应用程序,我能够发送跨域请求。尽管浏览器返回“跨源”错误,但我的服务器仍在接收和执行请求。例如,我可以从远程站点调用跨域请求

$.ajax({
        xhrFields: {
            withCredentials: true
        },
        data:{ my: 'a' },
        url: 'http://MyApp/Page',
        type: 'POST'
})

我知道浏览器不会返回对脚本的响应,但我的服务器页面仍在执行。

假设一个无辜的用户登录了一个站点http://example.com此应用程序将接受 POST 请求以插入记录。当无辜用户访问 http://HackerSite.com时,http://HackerSite.com将能够http://example.com通过 AJAX 发送 POST 请求。如何防止这种情况?

4个回答

除了通过带有凭据的跨域 AJAX 请求外,您的示例中的 POST 也可以使用不带 AJAX 的标准表单发送:

<form method="post" action="http://MyApp/Page" name="hiddenFormInIframe">

<input type="hidden" name="my" value="a" />

</form>
<script type="text/javascript">
  document.hiddenFormInIframe.submit();
</script>

这还将在请求中包含 cookie。

您所说的漏洞称为Cross Site Request Forgery

您可以通过在 AJAX 请求中发送标头然后检查标头服务器端来防止从另一个域提交 POST。一个常用的标头是X-Requested-With. 由于此标头不在安全列表中(未在您的服务器上启用CORS ) ,因此无法通过 AJAX 跨域发送此标头。HTML 表单也不能发送此标头。

只有以下标头允许通过 AJAX 跨域:

  • 接受
  • 接受语言
  • 内容-语言
  • 最后事件 ID
  • 内容类型

使用 JQuery 时会自动设置标X-Requested-With头,您可以在 ASP.NET 代码中检查这一点。

然而,在过去有一些通过插件(例如Flash)的漏洞利用,其中可以设置标头,而通过浏览器(例如Referer)是不可能的,因此为了防止这种情况,建议使用同步器令牌模式,该模式涉及在隐藏字段或标题中。将验证此令牌以及具有副作用的所有请求的身份验证 cookie。也就是说,更改、提交或删除的请求(通常是 POST)。

有关 CORS 的更多信息,请参见此处:http ://www.html5rocks.com/en/tutorials/cors/

跨域策略会阻止域在某些情况下从其他域中读取信息,这些域没有明确允许使用CORS 标头但是,这并不禁止跨域请求发出。您的示例中的请求应该是“飞行前”OPTIONS请求:一个探测请求,以查看“实际”请求是否会成功。

部分解决方案可能是OPTIONS在流程的早期终止请求:一旦可以确定探测域是否被允许访问。

更完整的解决方案可能需要 API 访问令牌。在每次页面加载时,生成并嵌入访问令牌。在所有 API/AJAX 请求中包含该令牌。任何不包含令牌的请求都应提前终止。

正如其他人所说,这里最好的想法是使用 CSRF 令牌。最简单的形式是将用户 IP 地址作为标识符添加到数据库中,然后为该 IP 添加一个随机值。随机值是正确的标记。

例如,当有人访问您的页面以获取表单时,获取用户 IP,例如:12.12.12.12。

与随机值一起添加到键值数据库中:12.12.12.12 = ljghsdlghdlghdlshsdflgd(注意,不要使用示例“ljghsdlghdlghdlshsdflgd”,而是生成足够长度的伪随机值)

将此伪随机值添加为隐藏字段。

提交时,使用用户 IP 查找建议的回复。如果实际回复与建议回复匹配,则接受请求。在任何尝试提交(无论成功与否)时,删除数据库中的记录(如果存在)。这意味着任何 CSRF 尝试都会使目标用户的真实令牌无效,并且每个令牌都只能使用一次。根据它是什么类型的数据库,它可以简单地将键设置为“”,也​​可以简单地删除它。

像:12.12.12.12 = ""。

一个好主意是按时间使这些值过期,这既是为了安全,也是为了摆脱数据库中不必要的数据。取决于它是否是一个论坛,将几个小时作为有效时间可能是明智的,而如果它是一个简单的形式,比如更改密码,那么大约 5 分钟作为有效时间就足够了。


另一个想法是验证码,它还可以防止自动客户端攻击,例如病毒感染和自动远程控制木马。根据 Web 应用程序的敏感性,同时使用验证码和 CSRF 令牌可能是一个好主意。不要遵循建议在整个会话中使用相同的令牌,这在安全方面是个坏主意,因为用户可能会被欺骗向攻击者提交令牌以及向站点提交令牌的可能性很小。一次性令牌对攻击者来说是无用的,但多用途令牌可能与会话一样危险,CSRF 旨在保护会话。

因此,使用一次性令牌,与多用途令牌相比,它是最好的安全性,而且一次性令牌也不需要像其他回复之一中的 CSRF 文章所建议的那样在传输过程中受到保护。

如果您已正确配置所有内容,则不应执行服务器页面。该请求应该在它有机会达到那么远之前被拒绝。

我怀疑您已将应用程序/服务器配置为Access-Control-Allow-Origin设置为*. 有很多地方可以做到这一点,但对于 asp.net 应用程序来说,一个常见的地方是 web.config(s)。例子:

<httpProtocol>
  <customHeaders>
    <add name="Access-Control-Allow-Origin" value="*" />
  </customHeaders>
</httpProtocol>

我会检查您的应用程序中是否有此设置。请记住,asp.net 可以有多个 web.config 文件,并将使用最接近的设置。例如,假设您具有以下目录结构:

/MyProject/ajax/stuff

如果您的MyProject目录中有 web.config 并且目录中有 web.config stuff,则其中的文件stuffstuff尽可能使用 web.config

让我知道这是否令人困惑。

另外,我相信这也可以在 IIS 中进行配置,因此也请查看您的标头。