• 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
ajax post csrf problem 403 error

#1
Hello everyone

I know this topic got discussed many time .. yet I could not make it run without problems

first of all .. I set csrf to true

PHP Code:
$config['csrf_protection'] = TRUE;  


now I included csrf token/value in the data sent by ajax
and it works for the first time ( as csrf is present and it is correct )
in the second time ajax tries to run .. I get 403 error ( as the sent csrf is still the same not changed ( not refreshed page ) )
i found that the first post will change the token value of csrf

how should I work with that and make it run ?

here is my code ( note that Iam using smary )

PHP

PHP Code:
        $this->vars['csrf'] = array(
        'name' => $this->security->get_csrf_token_name(),
        'hash' => $this->security->get_csrf_hash()
        ); 


HTML

Code:
<input type="text" class="form-control" name=atitle id=atitle>
<input id=csrf name="{$csrf.name}" type="hidden" value="{$csrf.hash}" />
<script>
$("#atitle").on('input propertychange paste change',function () {
    $.post("{$baseurl}Ajax/ar2eng",{ {$csrf.name}: $("#csrf").val(),title: $("#atitle").val() }).done(function( data ) {
     $("#aslug").val(data);
    });
  });
</script>
Reply

#2
Hi,

Set

<pre>$config['csrf_regenerate'] = FALSE;</pre>

in your application/config/config.php file. The reason your AJAX fails the second time is because the CSRF token regenerates with each new page request, causing the old token to become invalid.
Reply

#3
thansk ...
OK I got that
yet isn't it more secure to make csrf_regenerate true so it will regenerate every time ?
is this is the only solution ?
Reply

#4
It might be best to not turn of CSRF regeneration, otherwise you could be opening your site to some problems.

Another way to deal with this without turning off regeneration is to read the CSRF value in your javascript from a hidden field or a 'data-xxx' value somewhere on the page. Once you have used it, you can use ajax to get a new value and update it on your page. Or you can simply return the new value in your first ajax call and refresh a value from there.

https://www.codeigniter.com/user_guide/l...rgery-csrf

If you need any further help with this post here again and I will post some sample code.

Best wishes,

Paul.
Reply

#5
thank you PaulD
can you put a sample ?
Reply

#6
***** EDIT: A public get call to return the current hash is not a great idea. Return the new hash value as part of your original ajax call, not as a separate get. The following answer is just an example to get the idea across ******


So here is one way: (I am not saying this is the only way, the best way, or the most appropriate for your site, but it gets the idea across).

In your view:
Code:
<div id="csrf" data-token="<?php echo $this->security->get_csrf_hash(); ?>"></div>

In your js file
Code:
<script>

$("#atitle").on('input propertychange paste change', changeProperty);

function changeProperty(event) {
    event.preventDefault();
   
    /* collect data */
    var hashValue = $('#csrf').data('token');
    var aTitle = $("#atitle").val();
   
    /* create post data */
    var postData = {
         title: aTitle,
         mycsrf: hashvalue,
    };
    var url = '{$baseurl}Ajax/ar2eng';
   
    /* send post data */
    $.post(url,postData, function(theResponse) {
          /* you should do some validation of theResponse here first */
         $("#aslug").val(theResponse);
         /* refresh tokens on the page */
         refreshTokens();
   });
};

function refreshTokens() {
    var url = /* url to controller to return hash value */;
    $.get(url, function(theResponse) {
          /* you should do some validation of theResponse here too */
         $('#csrf').data('token', theResponse);
    });
}
</script>

In your controller that the refreshTokens function calls something like:

PHP Code:
public function get_tokens() {
 
// check user is logged in first of course
 
.....
 return 
$this->get_csrf_hash();


I have just typed this out so might be some typos in there. Where are you getting your {$base} from? I was assuming this was in a seperate js file loaded into the page with a script tag. If you are using placeholders with a template library loading a view for your js you may want to alter some of the above. If it is in a seperate file I would often do a similar read of the base url if it is going to be used in many sites, or hardcode a global variable in the js.

I have called your csrf token 'mycsrf' and hardcoded the name but you can always read in the token name via another data-token value in the 'csrf' div.

Anyway, I hope this helps and makes some sense.

Paul.

PS Just found this which is another explanation: https://forum.codeigniter.com/thread-612...#pid327081
Reply

#7
wow .. good thinking
thankyou @PaulD
I will apply this solution
but don't you think printing the new hash ( in controller/method ) and make it public is risky ?
Reply

#8
Not sure in truth. I might have done something dodgy in writing a quick example for you. It does look risky doing it this way, but I only did this as an example. I would return the new hash value with the original ajax call, and not do a seperate ajax get for it. (As MWhitney did in his explanation), but as I said, this is just an example of how to do it.

Actually doing it as a public get does feel a bit wrong. Perhaps we need someone with more security knowledge and CSRF and XSS understanding and expertise than I have, to let us know.

Good point though. This get example is probably not the best way.

If site B (the bad site) tries to access site A (the good site you are logged into) it will get a different hash value than the one you get on site A, if it tries to access the get value (as it is a different session). So I think it would be fine. Just make sure any state changing processes are done via a post, not a get. But TBH this is most certainly not an area I am knowledgeable in would appreciate any advice from any readers with more insight to advise about this.
EDIT: Actually the bad site now has a valid hash value and your cookie will say it is logged in. So it is not fine :-(

So IMHO return the new hash with the original ajax call, not as a separate function. At least then the new hash is only returned when the current hash is checked and regenerated.

Best wishes,

Paul

PS added an edit to the top of original sample post above.
Reply

#9
I agree with the idea of returning the regenerated hash with the original ajax call.

I don' t see how it is any more vulnerable than the original page display where anybody with a browser web dev tool can clearly see the token and hash. They'll also be able to see the new hash when the page is updated by the ajax return values.

For more than you wanted to know about CSRF read this.
Reply


Digg   Delicious   Reddit   Facebook   Twitter   StumbleUpon  


  Theme © 2014 iAndrew  
Powered By MyBB, © 2002-2020 MyBB Group.