protecting against CSRF on ajax requests and forms without submit buttons

14,925

Solution 1

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.

Solution 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 );
Share:
14,925
Anonymous
Author by

Anonymous

Updated on June 21, 2022

Comments

  • Anonymous
    Anonymous almost 2 years

    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?

  • DampeS8N
    DampeS8N about 12 years
    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.
  • Cheekysoft
    Cheekysoft about 12 years
    @DampeS8N Feel free to edit that in for completeness, but the OP has said he is comfortable with CSRF tokens in form posts.
  • Anonymous
    Anonymous about 12 years
    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
    Anonymous about 12 years
    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?
  • DampeS8N
    DampeS8N about 12 years
    @Anonymous it would depend on your language of choice. That looks like PHP, so I'd check something like getallheaders
  • DampeS8N
    DampeS8N about 12 years
    @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.
  • Anonymous
    Anonymous about 12 years
    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?
  • DampeS8N
    DampeS8N about 12 years
    @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.
  • Anonymous
    Anonymous about 12 years
    I think I'll need to set up SSL. Thanks for all your help so far!
  • Cheekysoft
    Cheekysoft about 12 years
    @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 stackoverflow.com/questions/541430/….