【问题标题】:Is an X-Requested-With header server check sufficient to protect against a CSRF for an ajax-driven application?X-Requested-With 标头服务器检查是否足以防止 ajax 驱动的应用程序的 CSRF?
【发布时间】:2011-03-19 22:59:29
【问题描述】:

我正在开发一个完全由 ajax 驱动的应用程序,其中所有请求都通过基本上相当于一个主控制器的东西,它的基本结构看起来像这样:

if(strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
    fetch($page);
}

这通常足以防止跨站点请求伪造吗?

当每次请求都没有刷新整个页面时,使用旋转令牌是相当不方便的。

我想我可以在每个请求中将唯一令牌作为全局 javascript 变量传递和更新——但不知何故,这感觉很笨拙,而且似乎天生不安全。

编辑 - 也许静态令牌,如用户的 UUID,总比没有好?

EDIT #2 - 正如 The Rook 所指出的,这可能是一个令人毛骨悚然的问题。我已经阅读了两种方式的猜测,并听到了关于旧版本的闪存可用于这种恶作剧的传言。由于我对此一无所知,因此我悬赏任何能够解释这是 CSRF 风险的人。否则,我会把它交给 Artefacto。谢谢。

【问题讨论】:

标签: php ajax security token csrf


【解决方案1】:

我会说这已经足够了。如果允许跨域请求,那么您无论如何都注定要失败,因为攻击者可以使用 Javascript 获取 CSRF 令牌并在伪造的请求中使用它。

静态令牌不是一个好主意。每个会话至少应生成一次令牌。

EDIT2 抱歉,毕竟迈克不对。我没有正确阅读我链接到的页面。它说:

一个简单的跨站点请求是:[...] 不使用 HTTP 请求设置自定义标头(如 X-Modified 等)

因此,如果你设置X-Requested-With,则请求必须是pre-flight,除非你响应pre-flight OPTIONS请求授权跨站请求,否则它不会通过。

编辑 Mike 是对的,从 Firefox 3.5 开始,跨站点 XMLHttpRequest 为 permitted。因此,您还必须检查 Origin 标头(如果存在)是否与您的网站匹配。

if (array_key_exists('HTTP_ORIGIN', $_SERVER)) {
    if (preg_match('#^https?://myserver.com$#', $_SERVER['HTTP_ORIGIN'])
        doStuff();
}
elseif (array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER) &&
        (strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'))
    doStuff(); 

【讨论】:

  • 致对我投了反对票的先生:你能解释一下我的回答有什么问题吗?
  • 这是一个糟糕的答案,因为只是在标题中添加一些内容绝对不会提供任何保护,因为它很容易被伪造。请参阅已接受的答案以获得解决问题的好方法。
  • @musicfreak 此标头仅在通过 Javascript 发出请求时添加。由于跨站点限制,CSRF 无法使用 Javascript 完成。因此,这就足够了。当然你可以“伪造”标题,例如通过构建一个始终发送此标头的浏览器。但是这样做,你会在脚上开枪。对 CRFS 的保护是对用户自己的保护,防止他们以恶意和程序方式发出的请求。
  • @musicfreak 这也是比较有名的方法。请参阅code.djangoproject.com/ticket/8127“假设这是对 X-Requested-By: XMLHttpRequest 标头的检查,我完全赞成 - 这是一种允许 Ajax 请求避免包含表单令牌的好、安全的方式,它可以是否则很难让 Ajax 代码可用。”
  • +1 你说得对,先生。别介意这些傻瓜,因为他们不知道自己在说什么。你读过谷歌浏览器安全手册吗?我想你有。
【解决方案2】:

我不相信这是安全的。同源策略旨在防止来自不同域的文档访问从不同域返回的内容。这就是为什么首先存在 XSRF 问题的原因。一般来说,XSRF 并不关心响应。它用于执行特定类型的请求,例如删除操作。 在最简单的形式中,这可以通过格式正确的 img 标签来完成。您提出的解决方案将阻止这种最简单的形式,但不会保护某人使用 XMLHttp 对象发出请求。 您需要使用 XSRF 的标准预防技术。我喜欢在 javascript 中生成一个随机数并将其添加到 cookie 和表单变量中。这确保代码也可以为该域编写 cookie。如果您想了解更多信息,请参阅this entry

另外,为了抢占关于 XMLHttp 不能在脚本中工作的 cmets。我将以下代码与 firefox 3.5 一起使用,从在 localhost 域中运行的 html 向 google 发出请求。内容不会返回,但是使用firebug,可以看到请求已经发出。

<script>
var xmlhttp = false; 

if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
    try {
        xmlhttp = new XMLHttpRequest();
    } catch (e) {
        xmlhttp = false;
    }
}
if (!xmlhttp && window.createRequest) {
    try {
        xmlhttp = window.createRequest();
    } catch (e) {
        xmlhttp = false;
    }
}

xmlhttp.open("GET", "http://www.google.com", true);
xmlhttp.onreadystatechange = function() {
    if (xmlhttp.readyState == 4) {
        alert("Got Response");
        alert(xmlhttp.responseText)
    }
}

xmlhttp.send(null)
alert("test Complete");

【讨论】:

  • 我把它交给了 Artefacto,因为它看起来像 X-Requested-With 没有通过,但这很有趣。谢谢。
【解决方案3】:

我认为这不会提供任何保护。攻击站点仍然可以使用xmlhttprequest 进行跨站点请求绕过您的检查。

【讨论】:

  • 你会碰巧知道一个合适的解决方案吗?你不必牵着我的手带我走过,但也许你可以为我指明正确的方向? :)
  • @Greg:不,不是。我在想使用一次性令牌是要走的路。但我没有这方面的经验。
【解决方案4】:

简短的回答:不。任何攻击者只会使用 Ajax 自己来攻击您的网站。 您应该生成一个生命周期较短但不会过多的随机令牌,您将在每个 ajax 请求期间对其进行更新。

您必须在 javascript 中使用一组标记,因为您可能同时运行多个 ajax 请求。

【讨论】:

  • 我的印象是不允许跨域 AJAX 请求?我想有解决方法吗?
  • 攻击者可以使用 Firebug 或其他开发者控制台在域内进行 AJAX。
  • -1,是否愿意编写一个漏洞利用来备份该评论?不?我不这么认为。 xmlhttprequest 不能跨站点,它违反了同源策略。它可能不是一种强大的反抗方法,但你不能为此编写漏洞利用程序。阅读谷歌浏览器安全手册。
  • "xmlhttprequest 不能跨站点",这是客户端。如果您的用户的浏览器出现问题,为什么不在服务器端尽您所能呢? Flash 也不是你的朋友。最后但并非最不重要的一点:您可能还有一些 XSS 易受攻击的页面,让您的客户端发送请求可能比窃取他的凭据更容易。
  • @SHiNKiROU 一个使用开发控制台在域内进行 AJAX 的攻击者仅仅是 CSRF 攻击自己,不是吗?
【解决方案5】:

您所做的是安全的,因为 xmlhttprequest 通常不易受到跨站点请求伪造的影响。

由于这是客户端问题,最安全的方法是检查每个浏览器的安全架构:-)

(这是一个总结;我添加这个答案是因为这个问题非常令人困惑,让我们看看投票怎么说)

【讨论】:

    【解决方案6】:

    不,这可以很容易地绕过, 通过向包含此标头和带有其凭据的请求的服务器发出跨域 Flash 请求, 看到这个:https://www.geekboy.ninja/blog/exploiting-json-cross-site-request-forgery-csrf-using-flash/?unapproved=6685&moderation-hash=91554c30888cfb21580f6873e0569da0

    防止 CSRF 的最佳方法是使 Header 或 Parameter 包含每个请求的密钥,

    【讨论】:

      猜你喜欢
      • 2015-04-23
      • 2011-06-23
      • 2010-11-27
      • 2012-09-01
      • 2011-12-31
      • 1970-01-01
      • 1970-01-01
      • 2010-10-12
      相关资源
      最近更新 更多