关于php:X-Requested-With头服务器检查是否足以防止ajax驱动的应用程序的CSRF?

Is an X-Requested-With header server check sufficient to protect against a CSRF for an ajax-driven application?

我正在开发一个完全由Ajax驱动的应用程序,其中所有的请求都通过一个基本上相当于主控制器的程序,它的基本结构如下:

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

这一般足以防止跨站点请求伪造吗?

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

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

编辑——也许静态令牌(比如用户的UUID)比什么都没有要好?

编辑2-正如Rook指出的,这可能是一个令人毛骨悚然的问题。我读过各种各样的猜测,也听到过一些遥远的耳语,说旧版本的flash可以用来对付这种恶作剧。因为我对此一无所知,所以我要对任何能解释这是一种CSRF风险的人发放奖金。否则,我就把它交给人造的。谢谢。


我说够了。如果跨域请求被允许,那么无论如何,你都会被判有罪,因为攻击者可以使用javascript来获取CSRF令牌,并在伪造的请求中使用它。

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

伊迪丝2麦克毕竟不对劲,对不起。我没有正确阅读我链接到的页面。上面写着:

A simple cross-site request is one that: [...]
Does not set custom headers with the HTTP Request (such as X-Modified, etc.)

因此,如果您设置了X-Requested-With,那么请求必须是预飞的,除非您对授权跨站点请求的预飞OPTIONS请求作出响应,否则无法通过。

编辑Mike是对的,从火狐3.5开始,允许跨站点的xmlhttprequest。因此,您还必须检查存在的Origin头是否与您的站点匹配。

1
2
3
4
5
6
7
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();


对。这是公认的CSRF保护方法。

https://www.owasp.org/index.php/cross-site_-request_-forgery_u(csrf)_-prevention_-craty_-sheet protecting_-rest_服务:_-use_-of-custom_-request_-headers


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

另外,要预先清空有关xmlhttp不在脚本中工作的注释。我在firefox 3.5中使用以下代码从本地主机域中运行的HTML向Google发出请求。内容不会被返回,但是使用Firebug,您可以看到请求已经发出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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");


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

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

(这是一个总结;我要添加这个答案,因为这个问题非常混乱,让我们看看投票结果如何)


简短回答:没有。任何攻击者都会使用Ajax攻击您的网站。您应该生成一个随机令牌,它的生存期很短,但不会太长,您将在每个Ajax请求期间对其进行更新。

您必须在JavaScript中使用一个令牌数组,因为您可能同时运行多个Ajax请求。


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