Is an X-Requested-With Header Server Check Sufficient to Protect Against a Csrf for an Ajax-Driven Application

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

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

What's the point of the X-Requested-With header?

A good reason is for security - this can prevent CSRF attacks because this header cannot be added to the AJAX request cross domain without the consent of the server via CORS.

Only the following headers are allowed across origins:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type

any others cause a "pre-flight" request to be issued in CORS supported browsers.

Without CORS it is not possible to add X-Requested-With to a cross domain XHR request.

If the server is checking that this header is present, it knows that the request didn't initiate from an attacker's domain attempting to make a request on behalf of the user with JavaScript. This also checks that the request wasn't POSTed from a regular HTML form, of which it is harder to verify it is not cross domain without the use of tokens. (However, checking the Origin header could be an option in supported browsers, although you will leave old browsers vulnerable.)

New Flash bypass discovered

You may wish to combine this with a token, because Flash running on Safari on OSX can set this header if there's a redirect step. It appears it also worked on Chrome, but is now remediated. More details here including different versions affected.

OWASP Recommend combining this with an Origin and Referer check:

This defense technique is specifically discussed in section 4.3 of
Robust Defenses for Cross-Site Request Forgery. However, bypasses of
this defense using Flash were documented as early as 2008 and again as
recently as 2015 by Mathias Karlsson to exploit a CSRF flaw in Vimeo.
But, we believe that the Flash attack can't spoof the Origin or
Referer headers so by checking both of them we believe this
combination of checks should prevent Flash bypass CSRF attacks. (NOTE:
If anyone can confirm or refute this belief, please let us know so we
can update this article)

However, for the reasons already discussed checking Origin can be tricky.

Update

Written a more in depth blog post on CORS, CSRF and X-Requested-With here.

How do you protect against XSRF in Grails?

You could try looking at the source code of the <g:form> tag. It uses a SynchronizerToken to create a token and store it in the session. Based on the resolution of this issue it should be possible to use the same token for all forms on the same page. I did not try this, but theoretically you would just need to manually create a hidden field on the form and generate the token in that field.

Preventing erroneous AJAX Calls by the user

You should handle that on the server-side, If you really want to prevent multi-vote or prevent the same people from voting several time on the same subject.
This is why real votes always use authenticated users and never anonymous votes.

By checking the request is really a XmlHttpRequest (with @Shaun Hare response code or with the linked stackoverflow question in your questions comments) you will eventually block some of the CSRF but you won't prevent a repost from the user, using tools like LiveHttpHeaders 'replay' and such. Everything coming from the client side can be forged, everything.

edit* if it's not a voting system as you commented, the problem is teh same, you nedd 'something' to know if the user is doing this action for the first time, or if he can still do this action. There's a lot of different things available.

You can set a token on your page, use that token in the ajax requests, and invalidate this token for later usage server side. This is one way. the problem is where to store these tokens server-side (sessions, caches, etc)

Another way is to check on the server side the situation is still a valid situation (for example a request asking to update 'something' should maybe handle a hash/marker/timestamp that you can verify with current server side state.

This is a very generic question, solutions depends on the reality of the 'performed action'.

Codeigniter CSRF valid for only one time ajax request

In my opinion you should try to recreate your csrf token each request

Try this code example...

For the js funcion

var csrfName = '<?php echo $this->security->get_csrf_token_name(); ?>',
csrfHash = '<?php echo $this->security->get_csrf_hash(); ?>';
("#avatar").change(function(){
var link = $("#avatar").val();

var dataJson = { [csrfName]: csrfHash, id: "hello", link: link };

$.ajax({
url : "<?php echo base_url('main/test'); ?>",
type: 'post',
data: dataJson,
success : function(data)
{
csrfName = data.csrfName;
csrfHash = data.csrfHash;
alert(data.message);
}
});
});

and for the controller

public function test() { 
$config['upload_path'] = './uploads/';
$config['allowed_types'] = 'gif|jpg|png';
$config['max_size'] = 500;
$config['max_width'] = 260;
$config['max_height'] = 260;

$reponse = array(
'csrfName' => $this->security->get_csrf_token_name(),
'csrfHash' => $this->security->get_csrf_hash()
)

$this->load->library('upload', $config);
if (!$this->upload->do_upload('link')) {
$reponse['message'] = "error";
}
else {
$data = array('upload_data' => $this->upload->data());
$image_name = $data['upload_data']['file_name'];
$reponse['message'] = $image_name;
}

echo json_encode($reponse);
}

Let me know and good luck

Note: When someone ask you for posting more data to the question, don't post it as a comment or answer, it's better to edit the question itself and adding the stuff

BeautifulSoup Not Parsing HTML Correctly inside Try/Except Loop

Since nobody took a crack at it I thought I would come back through and update on a solution - my use of "X-Requested-With": "XMLHttpRequest" in my head variable is what was causing the error. I'm still new to programming, especially with making HTTP requests, but I do know it has something to do with Ajax. Anyways, when I removed that bit from the headers attribute in my request BeautifulSoup parsed the document in full.

This answer as well as this one explains in a lot more detail that this is a common approach to prevent Cross Site Request Forgery, which is why my request was always coming back empty.

Simple example for why Same Origin Policy is needed

<iframe id="bank" src="https://yourbank.example"></iframe>

<script>
window.onload = function() {
document.getElementById('bank').contentWindow.document.forms[0].action =
'http://example.com';
};
</script>

The JavaScript code changes the form's action property (the destination, in a matter of speaking), so when you submit the form, you send your credentials to me, not your bank.

If I set up a PHP script on my server that redirects you to your bank, you won't even notice it.

With Same Origin Policy, this attack isn't possible. A site on my domain cannot read or modify the contents of the bank's website.



Related Topics



Leave a reply



Submit