Asked  7 Months ago    Answers:  5   Viewed   34 times

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.

 Answers

94

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(); 
Wednesday, March 31, 2021
 
RemiX
answered 7 Months ago
55

Now my main concern is, can't the HTTP headers be spoofed? For example, can't an attacker curl a request to the other server, sending the exact HTTP headers that my CORS request does?

You have two misconceptions here.

  1. CORS headers are sent by the server not the client (although sometimes a client will make a pre-flight OPTIONS request)
  2. What the Same Origin Policy is defending against

The Same Origin Policy exists to stop Mallory's (evil) website from getting data from Bob's website by asking Alice's browser to request it when Alice visits Mallory's website.

If that was possible, then Mallory could get any information that was supposed to be a shared secret between Alice and Bob (such as Alice's account balance on Bob's banking website).

can't an attacker curl a request to the other server, sending the exact HTTP headers that my CORS request does?

Since Mallory has no way of knowing what security credentials need to be included in the request (because, for instance, they are stored in Alice's cookies for Bob's website): No.

But CORS doesn't matter here, but the Same Origin Policy isn't implemented by cURL since it isn't a browser running JavaScript supplied by arbitrary websites.

I guess sensitive information should never be shared within a CORS communication

It depends on the nature of the information.

If Alice and whatever websites you authorise in the CORS headers are allowed to see it, then it is fine to send it (although you should probably use SSL): So long as you have authenticated Alice's identity.

If only Alice and you site should see it, then don't put CORS headers on it (and don't provide any other way to bypass the Same Origin Policy, such as JSON-P).

If Alice shouldn't see it, then you should never send it to Alice's browser, CORS or no CORS.

Saturday, May 29, 2021
 
Hilmi
answered 5 Months ago
98

know, that this should not be possible with XHR (see e.g. Security for cross-origin resource sharing), at least not, if we trust the W3C spec to be implemented correctly in all modern browsers (can we?)

At the end of the day you have to "trust" the client browser to safely store user's data and protect the client-side of the session. If you don't trust the client browser, then you should stop using the web at all for anything other than static content. Even with using CSRF tokens, you are trusting the client browser to correctly obey the Same Origin Policy.

While there have been previous browser vulnerabilities such as those in IE 5.5/6.0 where it has been possible for attackers to bypass the Same Origin Policy and execute attacks, you can typically expect these to be patched as soon as discovered and with most browsers automatically updating, this risk will be mostly mitigated.

But what about other kinds of requests - e.g. form submit? Loading a script/img/... tag? Or any other way a page can use to (legally) create a request? Or maybe some known JS hack?

The Origin header is normally only sent for XHR cross-domain requests. Image requests do not contain the header.

Note: I am not talking about

  • native applications,

  • manipulated browsers,

  • cross site scripting bugs in example.com's page,

I'm not sure whether this falls under manipulated browsers or not, but old versions of Flash allowed arbitrary headers to be set which would enable an attacker to send a request with a spoofed referer header from the victim's machine in order to execute an attack.

Sunday, June 20, 2021
 
daiscog
answered 4 Months ago
63

Among other things, using the referrer won't work for users whose browsers (or corporate proxies) don't send referrers.

Thursday, July 29, 2021
 
antoniputra
answered 3 Months ago
31

The HTTP method is supposed to be case-sensitive, but the HTTP headers are supposed to be case-insensitive, according to RFC 2616.

Sunday, September 26, 2021
 
FWH
answered 3 Weeks ago
FWH
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :