3

I have csrf enabled with codeigniter which is working properly in FireFox and Google Chrome. However in IE it's displaying this error in web developer tool network panel:

enter image description here

and in detailed view:

enter image description here

my $.post call is:

var ctn = $.cookie('csrf_cookie');
$.post('some_path', {
    'my_token': ctn
}, function (data) {
    if (data.res == 'something') {
        //process here
    }, 'json');

and the value of ctn which is the cookie that holds CSRF token value is displayed correctly when I do console.log('ctn: '+ctn) as I get:

ctn: 78346b5d0ec105efcce796f93ecc3cbb 

Any help or suggestion to debug more will be greatly appreciated.

P.S.: I have a vhost and I really don't know if it makes a difference with IE.

Update:

I have read about problems with CSRF in IE some suggested to use P3P headers, so I added this header to the index page:

header('P3P:CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT');

But still having same problem.

Any other suggestions?

mamdouh alramadan
  • 8,349
  • 6
  • 36
  • 53
  • Have you tried setting contentType: ``"application/json"`` for the request? On a sidenote: I encountered a multitude of problems that all came from IE caching ajax requests... – moritzpflaum Oct 29 '13 at 07:12
  • I already tried that. Not working – mamdouh alramadan Oct 29 '13 at 07:30
  • BTW, IE is catching the request but it's returning 500 due to csrf not being verified. – mamdouh alramadan Oct 29 '13 at 07:31
  • I got same problem with csrf so i directly use js in php file and pass data in ajax like data:{ 'security->get_csrf_token_name(); ?>':'security->get_csrf_hash(); ?>', 'start':convert(start), 'end':convert(end), 'allDay':allDay, 'title':'title' }, and its work fine for me – shafiq.rst Oct 31 '13 at 08:05

2 Answers2

3

As I suggest in my article. It is better to use the post in net.tutsplus with title: Protect a CodeIgniter Application Against CSRF . This is an old post and the solution is for Codeignter 1.7.x . However this is the only proper solution for Codeigniter 2 that I could find so far in order to achieve the CSRF protection in Codeigniter. The main issue that we have here is that Codeigniter uses the COOKIE . This post however uses the Codeigniter session that it is more safe to use and it is working for all the browsers without any issue. The article from net.tutsplus will work for AJAX and non AJAX request.

So my suggestion is that if you don't find a work around for your question, you can try to implement the article: Protect a CodeIgniter Application Against CSRF

John Skoumbourdis
  • 3,041
  • 28
  • 34
  • so which way you implemented with grocery crud. As GC is working in IE with csrf implemented. – mamdouh alramadan Oct 29 '13 at 21:35
  • In grocery CRUD I have the simple implementation of the csrf protection with the default installation of Codeigniter and it is not working for all IE! However this is not a bug of grocery CRUD, this is a bug of Codeigniter, so I can't force users to hack Codeigniter in order to make it work. That's why I took the time and write the article about "5 reasons to NOT use csrf_protection in Codeigniter". Some people liked the article but some people hate it. I know that the answer is not the answer that you wanted to hear but this is my opinion after struggling some days with csrf protection :) – John Skoumbourdis Oct 30 '13 at 17:05
  • 1
    +1,john - I respect you. Thus, I respect your opinion. in IE9 + IE10 it's working great and that's why I asked you so I might have the ability to apply the same way in my code. anyway, thanks for your answer and I'll keep my question open for now to see if anyone else would like to add on it. – mamdouh alramadan Oct 30 '13 at 21:02
  • what I found out is that IE post takes time to be initialized. Hence, the csrf won't work with. and that's why if you add `usleep(10)` before calling the csrf in CI core this will result a debug log saying csrf token recognized or somehing. however it will not work anyway with IE :( – mamdouh alramadan Nov 04 '13 at 20:25
  • Hey @mamdouhalramadan what about my solution? Did you try to implement a custom library such as the one here? http://net.tutsplus.com/tutorials/php/protect-a-codeigniter-application-against-csrf/ I think this is the only solution that it is left for you to do. What do you think? – John Skoumbourdis Nov 05 '13 at 12:38
  • 2
    Hi, I did implement it, and I posted an answer explaining the exact issue. Thanks for your help. :D – mamdouh alramadan Nov 15 '13 at 10:00
  • 1
    Hello @mamdouhalramadan , I am really happy that I could help you to this one. Also I am more happy that at last there is one proper answer at the so common question "codeigniter csrf with ajax not working in IE?". I think your answer will help someone else as well. – John Skoumbourdis Nov 15 '13 at 17:06
  • Thanks again. And I was hoping to get your Twitter account or any other preferable communication media for questions related to GC if it's not a problem with you :-) – mamdouh alramadan Nov 16 '13 at 06:46
  • 1
    Yes sure, all the social media for grocery CRUD is at the right of the footer of: http://www.grocerycrud.com/ ;-) There is fb, g+, twitter and github :) – John Skoumbourdis Nov 16 '13 at 09:35
2

Thanks to @John with the suggestion to implement CSRF by myself, I passed the first Issue. However, it turned out that IE is not submitting the post data at all (after debugging) So, there is a question on stackoverflow with the title IE is refusing to send data through $.ajax and to solve this problem you have to add this meta tag to tell IE to work with javascript in IE9 compatibility mode.

<meta http-equiv="x-ua-compatible" content="IE=9" >

Now, for the article about using hooks to work around csrf, it misses one issue which is, if you are using .serializeArray() or any equivalent in jquery to submit your form, you need to modify the
validate_tokens function to check for the token_name inside the posted array.

Hope this will save someone having the same problem

Note: adding the meta tag without rewriting the csrf will not solve the problem.

Update 1:

Here's the Implementation I'm using:

<?php
/**
 * Description of csrf_protection
 * @author Ian Murray
 */
class Csrf_Protection {

    private $CI;
    private static $token_name = 'somename';
    private static $token;

    public function __construct() {
        $this->CI = &get_instance();
    }

/** 
 * Generates a CSRF token and stores it on session. Only one token per session is generated. 
 * This must be tied to a post-controller hook, and before the hook 
 * that calls the inject_tokens method(). 
 * 
 * @return void 
 * @author Ian Murray 
 */ 
    public function generate_token()  
    {  
          // Load session library if not loaded  
          $this->CI->load->library('session');  

          if ($this->CI->session->userdata(self::$token_name) === FALSE)  
          {  
            // Generate a token and store it on session, since old one appears to have expired.  
            self::$token = md5(uniqid() . microtime() . rand());

            $this->CI->session->set_userdata(self::$token_name, self::$token);  
          }  
          else  
          {  
            // Set it to local variable for easy access  
            self::$token = $this->CI->session->userdata(self::$token_name);  
          }  
    }  

    /** 
    * Validates a submitted token when POST request is made. 
    * 
    * @return void 
    * @author Ian Murray 
    */  
   public function validate_tokens()  
   {  
     // Is this a post request?  
     if ($_SERVER['REQUEST_METHOD'] == 'POST')  
     {  
       // Is the token field set and valid?  
       $posted_token = $this->CI->input->post(self::$token_name);  
       if($posted_token === FALSE){
           $posted_token = $this->_get_token_in_post_array($this->CI->input->post());
           $this->_check_all_post_array($posted_token);
       }
     }  
   }
   /**
   *takes the posted token and check it after multidimesional-array search
   *@params $posted_token
   *@author Mamdouh Alramadan
   */
   private function _check_all_post_array($posted_token)
   {
       if ($posted_token === 'error' || $posted_token != $this->CI->session->userdata(self::$token_name))  
       {  
         // Invalid request, send error 400.
         show_error('Request was invalid. Tokens did not match.', 400);  
       }  
   }


   /** 
    * This injects hidden tags on all POST forms with the csrf token. 
    * Also injects meta headers in <head> of output (if exists) for easy access 
    * from JS frameworks. 
    * 
    * @return void 
    * @author Ian Murray 
    */  
   public function inject_tokens()  
   {  
     $output = $this->CI->output->get_output();  

     // Inject into form  
     $output = preg_replace('/(<(form|FORM)[^>]*(method|METHOD)="(post|POST)"[^>]*>)/',  
                            '$0<input type="hidden" name="' . self::$token_name . '" value="' . self::$token . '">',   
                            $output);  

     // Inject into <head>  
     $output = preg_replace('/(<\/head>)/',  
                            '<meta name="cname" content="' . self::$token_name . '">' . "\n" . '<meta name="cval" content="' . self::$token . '">' . "\n" . '$0',   
                            $output);  

     $this->CI->output->_display($output);  
   }  


/**
 * takes the posted array and check for the token inside it 
 * @params $arr array
 * @author Mamdouh Alramadan
 */
   private function _get_token_in_post_array($arr)
   {//this function is customized to my case but it's easy to adapt
       if(is_array($arr)){
        $key = $this->_recursive_post_array_search(self::$token_name, $arr);//this will return data if token found
        if($key === 'data'){//I'm expecting the token inside data array
            $key = $this->_recursive_post_array_search(self::$token_name, $arr['data']);
            return isset($arr['data'][$key]['value'])?$arr['data'][$key]['value']:FALSE;
        }
       }
       return 'error';
   }
   //some custom function to do multi-dimensional array search, can be replaced with any other searching function.
   private function _recursive_post_array_search($needle,$haystack) {
        foreach($haystack as $key=>$value) {
            $current_key=$key;
            if($needle===$value OR (is_array($value) && $this->_recursive_post_array_search($needle,$value) !== false)) {
                return $current_key;
            }
        }
        return false;
    }

}

?>
Community
  • 1
  • 1
mamdouh alramadan
  • 8,349
  • 6
  • 36
  • 53