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

发布于 2024-09-11 02:01:19 字数 521 浏览 4 评论 0原文

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

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

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

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

我想我可以通过每个请求将唯一令牌作为全局 javascript 变量传递和更新——但不知怎的,这感觉很笨拙,而且似乎本质上不安全。

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

编辑 #2 - 正如 The Rook 所指出的,这可能是一个令人吹毛求疵的问题。我读过这两种方式的猜测,并听到远处传言称旧版本的闪存可用于此类恶作剧。由于我对此一无所知,因此我向任何能够解释这是 CSRF 风险的人提供悬赏。否则,我会把它交给Artefacto。谢谢。

I'm working on a completely ajax-driven application where all requests pass through what basically amounts to a main controller which, at its bare bones, looks something like this:

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

Is this generally sufficient to protect against cross-site request forgeries?

It's rather inconvenient to have a rotating token when the entire page isn't refreshed with each request.

I suppose I could pass and update unique token as a global javascript variable with every request -- but somehow that feels clumsy and seems inherently unsafe anyway.

EDIT - Perhaps a static token, like the user's UUID, would be better than nothing?

EDIT #2 - As The Rook pointed out, this might be a hair-splitting question. I've read speculation both ways and heard distant whispers about older versions of flash being exploitable for this kind of shenanigans. Since I know nothing about that, I'm putting up a bounty for anyone who can explain how this is a CSRF risk. Otherwise, I'm giving it to Artefacto. Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

浅唱ヾ落雨殇 2024-09-18 02:01:19

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

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

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

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

因此,如果设置了 X-Requested-With,则该请求必须预先发送,除非您响应如果在飞行前 OPTIONS 请求授权跨站点请求,则该请求将无法通过。

编辑迈克是对的,从 Firefox 3.5 开始,跨站点 XMLHttpRequest 是允许的。因此,您还必须检查 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(); 

I'd say it's enough. If cross-domain requests were permitted, you'd be doomed anyway because the attacker could use Javascript to fetch the CSRF token and use it in the forged request.

A static token is not a great idea. The token should be generated at least once per session.

EDIT2 Mike is not right after all, sorry. I hadn't read the page I linked to properly. It says:

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

Therefore, if you set X-Requested-With, the request has to be pre-flown, and unless you respond to pre-flight OPTIONS request authorizing the cross-site request, it won't get through.

EDIT Mike is right, as of Firefox 3.5, cross-site XMLHttpRequests are permitted. Consequently, you also have to check if the Origin header, when it exists, matches your site.

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(); 
转身以后 2024-09-18 02:01:19

我不相信这是安全的。同源策略旨在防止来自不同域的文档访问从不同域返回的内容。这就是 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");

I do not believe that this is safe. The same origin policies are designed to prevent the documents from different domains from accessing the content that is returned from a different domain. This is why XSRF problems exist in the first place. In general XSRF doesn't care about the response. It is used to execute a specific type of request, like a delete action.
In the simplest form, this can be done with a properly formatted img tag. Your proposed solution would prevent this simplest form, but doesn't protect someone from using the XMLHttp object to make the request.
You need to use the standard prevention techniques for XSRF. I like to generate a random number in javascript and add it to the cookie and a form variable. This makes sure that the code can also write cookies for that domain. If you want more information please see this entry.

Also, to pre-empt the comments about XMLHttp not working in script. I used the following code with firefox 3.5 to make a request to google from html running in the localhost domain. The content will not be returned, but using firebug, you can see that the request is made.

<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");

终遇你 2024-09-18 02:01:19

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

I do not think this offers any kind of protection. An attacking site could still use xmlhttprequest for its cross-site request bypass your check.

檐上三寸雪 2024-09-18 02:01:19

简短的回答:不。任何攻击者都会使用 Ajax 本身来攻击您的网站。
您应该生成一个生命周期较短但不太长的随机令牌,您将在每个 ajax 请求期间更新该令牌。

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

Short answer : no. Any attacker would just use Ajax himself to attack your website.
You should generate a random token with a short but not too much lifetime which you would update during each ajax request.

You'd have to use an array of tokens in javascript as you may have multiple ajax request running at the same time.

ペ泪落弦音 2024-09-18 02:01:19

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

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


(这是一个总结;我添加这个答案是因为这个问题很令人困惑,让我们看看投票结果如何)

What you are doing is secure because xmlhttprequest is usually not vulnerable to cross-site request forgery.

As this is a client side problem, the safest way would be to check the security architecture of each browser :-)


(This is a summary; I am adding this answer because this question is very confusing, let's see what the votes say)

把人绕傻吧 2024-09-18 02:01:19

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

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

No this can be easily bypassed ,
By making A Cross-domain-Flash request to the server that contains this header and the request with it's credentials ,
see this : https://www.geekboy.ninja/blog/exploiting-json-cross-site-request-forgery-csrf-using-flash/?unapproved=6685&moderation-hash=91554c30888cfb21580f6873e0569da0

The best way to protect against CSRFs is to make Header or Parameter contains a secret key for each request ,

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文