X-Request-With 标头服务器检查是否足以防止 ajax 驱动应用程序的 CSRF?

2022-08-30 14:58:13

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

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

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

当整个页面未随每个请求刷新时,使用旋转令牌相当不方便。

我想我可以在每个请求中将唯一令牌作为全局javascript变量进行传递和更新 - 但不知何故,这感觉很笨拙,并且似乎本质上是不安全的。

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

编辑#2 - 正如The Rook指出的那样,这可能是一个令人毛骨悚然的问题。我读过两种方式的猜测,并听到了关于旧版本的flash可以利用这种恶作剧的遥远的耳语。由于我对此一无所知,因此我正在为任何能够解释这是CSRF风险的人提供赏金。否则,我会把它交给Artefacto。谢谢。


答案 1

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

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

编辑2迈克毕竟不对劲,对不起。我没有正确阅读我链接到的页面。它说:

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

因此,如果设置 ,则该请求必须预先飞行,除非您响应授权跨站点请求的预飞行请求,否则它不会通过。X-Requested-WithOPTIONS

编辑Mike是对的,从Firefox 3.5开始,允许跨站点XMLHttpRequests。因此,您还必须检查标题(如果存在)是否与您的网站匹配。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(); 

答案 2

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

此外,为了抢占有关 XMLHttp 在脚本中不起作用的注释。我在firefox 3.5中使用了以下代码,从本地主机域中运行的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");


推荐