7

Without going into too much details I have a site which is 100% Ajax. All requests to the site (both GET and POST) are done via Ajax. Now I have to implement CSRF protection, and all the solutions I came across boil down to sending a CSRF token in the headers, but most of them get this token from either HTML or a cookie that came with the GET request.

Now as my site is 100% Ajax and it doesn't reload. I wonder if I could, before each POST request, make a GET request to the server to get token and then submit it in the headers along with the POST request, for example

Submit GET request to aaa.com/get-csrf-token to get the token and then submit that token along with the request in the headers, if someone from another site makes a GET request to aaa.com/get-csrf-token and the server returns a token, then as far as I know they are still not able to read that value.

As the token is generated on a per session basis, I can simply check that the submitted token is the same token I have in the memchache which is associated with that session.

Could someone tell me if this would work?

S.L. Barth
  • 5,486
  • 8
  • 38
  • 47
Dave
  • 73
  • 1
  • 3
  • First, you are paying RTT twice for _every_ request, which is a big performance cost (https://docs.google.com/viewer?a=v&pid=sites&srcid=Y2hyb21pdW0ub3JnfGRldnxneDoxMzcyOWI1N2I4YzI3NzE2). Also, are you keeping an array of tokens in session? If not how would you handle multiple (obviously async) requests? – Mike S Jan 09 '13 at 19:04

3 Answers3

5

Yes, you could do this. This should be fine. It is a reasonable defense against CSRF.

Do make sure that side-effecting actions can only happen via POSTs (never via GET), and that all POSTs check for the presence of the token. Also, make sure you generate the token as a cryptographically-strong pseudorandom number (e.g., using CryptGenRandom(), /dev/urandom, SecureRandom, or similar) and store it in session state.

I don't know if this has any benefit over, say, cookie double-submission -- but you could do it, and it should protect against CSRF successfully.

D.W.
  • 98,420
  • 30
  • 267
  • 572
2

Really, two requests for each task is a performance overhead. You can simply achieve this using single request. You can simply validate the token during POST request, and send back the new token with each request. Using JS make sure you pick up the latest cookie value and attach it to every request AJAX sent.

In fact if you want to cut off your pain in CSRF protection of your web application, you can see OWASP CSRF Protector Project, (available for php only currently), it does everything required, to mitigate CSRF and works well with AJAX (both XHR and activeXObject)

mebjas
  • 121
  • 2
0

Instead of get a request before every request- how about this ?

  <script>
        var current_token=<?php echo $_SESSION['csrf_token']; ?>

            $.ajax({ 
                type: "POST",
                url:"script.php",
                data: {'token':current_token},
                success: function(data) {
                                var result=JSON.parse(data);
                                current_token=result.token;
                            },
                error:function(error){
                                console.log(error);
                }
          });
  </script>

here is script.php

 <?php

     if($_POST['current_token']==$_SESSION['csrf_token']){

          //do your thing 
          $token="new token" //generate new token here
          $_SESSION['token']=$token;
          $response['token']=$token;
          $out=json_encode($response);
          die ($out);       
    }else{ 
         //request failed - handle as error and ask to re-login or whatever 
    }

explanation-

  • You send your AJAX, validate the session token and post token
  • If successful, generate new token
  • Update the token in $_SESSION['token'] and also send the new token back
  • Use the success function retrieve the token and update in your JavaScript

Or, does this create a new vulnerability since the new token could be spoofed by XSS ?