6

I'm fairly new to web security. I was wondering what would be the correct way to use tokens on ajax requests and forms without submit buttons (ie status updates) to protect agains CSRF. Could someone show me a code sample? I know how to do it properly for forms with a submit button. Also in my ajax folder I have a htaccess with the following:

SetEnvIfNoCase X-Requested-With XMLHttpRequest ajax
Order Deny,Allow
Deny from all
Allow from env=ajax

Is that enough security for ajax requests, or should i also implement token security for ajax requests?

Ram
  • 143,282
  • 16
  • 168
  • 197
Anonymous
  • 179
  • 2
  • 3
  • 10

2 Answers2

10

All you need to do is generate a secret (token) when the page loads. Place it on the page, and in session. Typically I put it in the form as a hidden field if I can, or if it is part of a larger very ajaxy page, I'll put it in a value attribute on a related element like the div wrapping the whole page. Others will add a script tag with a variable set to the secret.

Then, whenever you make an AJAX call or send the form, you include a field for the secret. (Cheekysoft's answer goes into more detail about how to do this with raw JS, it can be done roughly the same with jQuery or any other framework you might be using.) Match it to the session value on the back end to ensure they are still the same (came from the same source), and you are good.

If you want added security, regenerate the secret on each request and return that new secret along with the requested data. Have all your ajax requests replace that hidden field or value attribute with the new value. This doesn't really work well if you are performing lots of requests, or concurrent requests. Really, generating one each time the whole page is loaded should be enough if you are doing this over HTTPS, which you should be.

If you aren't doing this over HTTPS, then it really doesn't matter, someone sitting in an internet cafe / starbucks can just steal the session and reload the page.

If you are going to be doing this a lot, it is worth checking out jQuery and various plugins that will help do the CSRF protection for you. Also, my way isn't the only way.

DampeS8N
  • 3,621
  • 17
  • 20
  • 1
    Thanks for the response. If I send it as request header as per one of your links: `var csrf_token = '=$ajaxtoken_value?>'; $("body").bind("ajaxSend", function(elm, xhr, s){ if (s.type == "POST") { xhr.setRequestHeader('X-CSRF-Token', csrf_token); } });` How would i check on the ajax side? would i have to retrive the token using a post request? – Anonymous Apr 13 '12 at 16:17
  • @Anonymous it would depend on your language of choice. That looks like PHP, so I'd check something like [getallheaders](http://php.net/manual/en/function.getallheaders.php) – DampeS8N Apr 13 '12 at 18:28
  • I figured out how to check it: `if($_SERVER['HTTP_X_CSRF_TOKEN'] == $_SESSION['ajaxtoken_value']) {` thanks. the only problem is that the request is over http. how would i change my settings so that request is done over https? – Anonymous Apr 13 '12 at 20:15
  • @Anonymous Do you already have other stuff that goes over ssl? (https) If so, probably you just move the file to the httpsdocs folder and change http to https in the request URL. If not. That's a question for the internets as it is involved to setup SSL. – DampeS8N Apr 13 '12 at 20:21
  • I think I'll need to set up SSL. Thanks for all your help so far! – Anonymous Apr 13 '12 at 20:30
2

If your XHR is a GET request, include the token as a URL parameter and have the PHP script read it from $_GET[]. If your XHR is a POST, pop the token into the POST data and have the PHP script read it from $_POST[].

GET request

var token = getElementById( 'myHiddenField' ).value;
var xhr = new XMLHttpRequest();
xhr.open( 'GET', 'server.php?token=' + token );
xhr.send( null );

POST request

var token = getElementById( 'myHiddenField' ).value;
var xhr = new XMLHttpRequest();
xhr.open( 'POST', 'server.php', true );
xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' );
xhr.onreadystatechange = function() {
  if (xhreq.readystate != 4) { return; }
  // do client side stuff here
};
xhr.send( 'token=' + token );
Cheekysoft
  • 35,194
  • 20
  • 73
  • 86
  • You should probably also include some notes on when and where to generate the token. And how to ensure you are checking against the same one on both ends. – DampeS8N Apr 13 '12 at 15:34
  • @DampeS8N Feel free to edit that in for completeness, but the OP has said he is comfortable with CSRF tokens in form posts. – Cheekysoft Apr 13 '12 at 16:02
  • Thanks for the response. If I send it as request header : var csrf_token = '=$ajaxtoken_value?>'; $("body").bind("ajaxSend", function(elm, xhr, s){ if (s.type == "POST") { xhr.setRequestHeader('X-CSRF-Token', csrf_token); } }); How would i check on the ajax side? would i have to retrive the token using a post request? – Anonymous Apr 13 '12 at 16:18
  • @Cheekysoft true, but the 100's of people that will come here from google might need the info. And now is the time to nip bad practices in the bud. – DampeS8N Apr 13 '12 at 18:30
  • 1
    @Anonymous It's a nice touch to set the token as an additional HTTP header in the client-side javascript call. On the server side you can read that in PHP using the methods in this question http://stackoverflow.com/questions/541430/how-do-i-read-any-request-header-in-php. – Cheekysoft Apr 16 '12 at 08:34