How does csrf-Protection work in Codeigniter?

Asked

Viewed 951 times

0

Today while working on a codeigniter project, I came across the concept CSRF - Cross-site request forgery. This happened when I got error 403 - permission denided when making a GET request in AJAX from VIEW to MODEL.

After investigating the matter, some doubts arose.

  1. csrf Protection is only used for requests in forms?
  2. How it works for requests without form submission?
  3. Where part of the request is hosted the hash-Protection token?

It’s still new to me... But as I noticed, the codeigniter generates through a hash function, a token that prevents the website of the attacker can put the token directly in their claims, this token is accessible in

$this->security->get_csrf_hash();

Now, according to my reading, we will have to use the token in the request, so that it can be compared by the token of the codeigniter. How do we send the token in the request?

[My Code]

[view]

// este request é feito quando desperto um evento choicelist.onchange()
$.ajax({
    url: CFG.url + "/application/models/Agenda_model/getAgendaDate",
    type: "get", //send it through get method
    data: { 
        date: '02-02-2019'
    },
    success: function(response) {
        console.log(response);
    },
    error: function(xhr) {
        //Do Something to handle error
    }
});
// in footer.php, gera o primeiro token
var CFG = {
    url: '<?php echo $this->config->item('base_url');?>',
    token: '<?php echo $this->security->get_csrf_hash();?>'
};
// after request, guarda token sucessores
$(document).ready(function($){
    $.ajaxSetup({data: {token: CFG.token}});
    $(document).ajaxSuccess(function(e,x) {
        var result = $.parseJSON(x.responseText);
        $.ajaxSetup({data: {token: result.token}});
    });
});

[Model]

public function getAgendaDate(){
        $sql = 'select * from agenda';
        $result = $this->db->query($sql);
        $send = array('token' => $this->security->get_csrf_hash()) + $result;

        if (!headers_sent()) {
            header('Cache-Control: no-cache, must-revalidate');
            header('Expires: ' . date('r'));
            header('Content-type: application/json');
        }
        // envia o novo token
        exit(json_encode($send, JSON_FORCE_OBJECT));
}

I believe my code has a small flaw. However I am receptive to new approach, a new design Pattern. How do I solve error 403?

[config.php]

$config['csrf_protection'] = TRUE;
$config['csrf_token_name'] = 'token';
$config['csrf_cookie_name'] = 'csrf_cookie_name';
$config['csrf_expire'] = 7200;
$config['csrf_regenerate'] = TRUE;
$config['csrf_exclude_uris'] = array();

[update] - 06-03-2019

I changed the ajax request from VIEW to Controller. I got the same error:GET http://localhost/Calendar/application/controllers/Basecontroller/getAgendaDate? date=02-02-2019 403 (Forbidden) Then I made another change based on in this documentation on Urls. I changed the URL to http://localhost/name_project/name_controller/method I got the GET error http://localhost/Calendar/basecontroller/getagendadate? date=02-02-2019&token=c058ce37a1246f496be4e8d1937b61b5 404 (Not Found)

[Directory structure]

calendar -
         - application
                       - cache
                       - config
                       - controllers
                                     - BaseController.php
                       - core
                       - helpers
                       - hooks
                       - language
                       - libraries
                       - logs
                       - models
                                - Agenda_model.php
                       - third_party
                       - views
                               - home.php
                       - .htaccess
                       - index
         - assets
         - system
         - .editorconfig
         - .gitignore
         - composer
         - contributing.md
         - index.php

[config.php]

$config['base_url'] = 'http://localhost/calendar';
$config['index_page'] = 'index.php';
$config['uri_protocol'] = 'REQUEST_URI';
$config['url_suffix'] = '';
$config['language'] = 'english';
$config['charset'] = 'UTF-8';
$config['enable_hooks'] = FALSE;
$config['subclass_prefix'] = 'MY_';
$config['composer_autoload'] = FALSE;
$config['enable_query_strings'] = FALSE;
$config['controller_trigger'] = 'c';
$config['function_trigger'] = 'm';
$config['directory_trigger'] = 'd';
$config['allow_get_array'] = TRUE;
$config['csrf_protection'] = TRUE;
$config['csrf_token_name'] = 'token';
$config['csrf_cookie_name'] = 'csrf_cookie_name';
$config['csrf_expire'] = 7200;
$config['csrf_regenerate'] = TRUE;
$config['csrf_exclude_uris'] = array();
  • Dude, just an addendum. A View should not communicate directly with the layer Model. In architecture MVC the correct is to use an intermediary for this, ie the Controller.

  • If you take a good look you’re exposing the structure of framework and also your Model: /application/models/Agenda_model/getAgendaDate.

  • @Pauloimon thanks for the tip. I’ve noticed this, I came from java, and in java the view gets the model.

  • Yes, I will use it in Controller. Does it solve error 403?

  • Right. I believe that’s the reason for the 403 yes.

  • @Pauloimon, just one more question. The most appropriate method to access the model loaded in the controller is using a view javascript request for the controller? Or is there another alternative?

  • Of View to the Controller would have no problem using a request AJAX. The Codeigniter maps access to resources through segments, that is, it would be the equivalent of typing the URL in the browser’s address bar.

  • In the CI you should not use the URL directly in that way: http://localhost/myTasks/application/controllers/BaseController/getAgendaDate?date=02-02-2019. All access via URL is redirected to the file index.php at the root of the application. The structure of Urls are like that: example.com/class/function/ID, I mean, it would look something like this in your case: http://localhost/myTasks/basecontroller/getagendadate?date=02-02-2019.

  • I tried, I had error 404 page not found. My base url is http://localhost:80/myTasks/application/controllers'

  • @Pauloimon print_r($this->Uri->segment_array()); returns Array(). There is something?

  • Try to change yours $config['base_url'] for http://localhost/mytasks.

  • I got the error, jquery.min.js:2 GET http://localhost/mytasks/basecontroller/getagendadate? date=02-02-2019 404 (Not Found)

  • @@Pedrocorreia updates your question, shows how the directory structure and the names of Controllers.

Show 9 more comments

1 answer

1

1. csrf Protection is only used for requests in forms?

No. The very documentation says it serves for any request that is not a GET-HTTP:

Codeigniter provides CSRF Protection out of the box, which will get Automatically Triggered for Every non-GET HTTP request, but also needs you to create your Submit Forms in a Certain way. This is Explained in the Security Library Documentation.

Basically: affects all requests $POST, and also applies to FORMS that they use $POST.

When you activate the use of Cross-site request forgery (CSRF) in the configuration of CodeIgniter (see here), it is recommended that you build your FORMS using the Form Helper, Because this will create a field of the type Hidden that will recover this hash automatically for you every time FORM is reloaded (you can create this field manually in each FORM).

2. How it works for requests without form submission?

First you need to set the basic settings in your application/config/config.php. I did something like this:

$config['csrf_protection'] = TRUE;
$config['csrf_token_name'] = 'ci_adminlte_csrf';
$config['csrf_cookie_name'] = 'csrf';
$config['csrf_expire'] = 7200;
$config['csrf_regenerate'] = TRUE;
$config['csrf_exclude_uris'] = array(
    'controllerName/methodName'
);

You see the variable $config['csrf_token_name']? This will be the name of the variable in the system, IE, ci_adminlte_csrf. Knowing this, know also something else: the CodeIgniter has 2 native methods to recover the name and value of this token/hash: get_csrf_token_name() and get_csrf_hash() (see here):

If you use the form helper, then form_open() will Automatically Insert a Hidden csrf field in your Forms. If not, then you can use get_csrf_token_name() and get_csrf_hash()

I mean, to get the name back: $name = $this->security->get_csrf_token_name(), and to recover the current value: $hash = $this->security->get_csrf_hash().

The secret of requests without FORM: You have to load the current hash value to the browser. PHP happens in the backend, then, every time the value of the hash is updated, the browser does not know unless you tell it. I use a variable JavaScript that recovers the present value for me.

To get the current value on PHP, use a method in any controller:

/**
 * Recupera o hash CSRF
 * @return string Valor do hash armazenado na memória
 */
function get_csrf_hash(){
    echo $this->security->get_csrf_hash();
}

Using the code: echo $ci->security->get_csrf_token_name()."='".$ci->security->get_csrf_hash()."';\n";. you can create something like this: ci_adminlte_csrf='909a7d6f8450a7a0d13db960184d050b';. I put this in a helper and I did it here:

<!--Variaveis globais-->
<script type='text/javascript'>
ci_adminlte_csrf='909a7d6f8450a7a0d13db960184d050b';
function get_csrf_hash(){
    $.get("controller/get_csrf_hash",
        function (resp) {
            ci_adminlte_csrf = resp;
            $("input[name*='ci_adminlte_csrf']").val(resp);
        }
    );
}
</script>

At each request without FORM call on the Function JavaScript get_csrf_hash() to reload both the variable and the input by name ci_adminlte_csrf.

Remark: THE config.php of the application has the variables $config['cookie_prefix'] and $config['csrf_cookie_name'] that serve to create a cookie that instantiates the value of token/hash csrf. So, if you need, you can just do the JavaScript recover the value of this cookie that is already automatically saved by the application. Look in the browser’s cookie list.

3. Where part of the request is the hash-Protection token?

It depends. On us requests with FORM has a field Hidden that carries the value of hash. If you’re not building the FORMS with the Form Helper will need to create this field within the FORM, and the Function JavaScript get_csrf_hash() will help you.

In the requisitions without FORM this is described in the answer to question 2 above. Read carefully that you will understand where it is stored and how to recover the name and value of token/hash, and the Function JavaScript get_csrf_hash() will help you again.

That’s it.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.