EXTJS CSRF protection
Asked Answered
T

1

11

I am trying to implement protection in one app against CSRF.

In PHP it is relatively simple to implement. I have many questions about how to do it with Extjs.

The EXTJS books I read do not address the subject and I can not find concrete guidance on this subject - with EXTJS - on the internet.

Some questions:

Using PHP, the token is sent to the EXTJS?

Do I have to create a hidden field in every form as in PHP?

Do I have to send to the server side the token in an Ext.Ajax.requestt? How to do this?

Some very simple code as a starting point:

class Token: https://www.youtube.com/watch?v=VflbINBabc4

<?php

 class Token {

 public static function generate() {
    $_SESSION['token'] = base64_encode(openssl_random_pseudo_bytes(32));
 }

 public static function check($token) {
    if(isset($_SESSION['token']) && $token === $_SESSION['token']){
        unset($_SESSION['token']);
        return true;
    }
    return false;
 }
}
?>

Query

<?php

require('conect.php');

require_once('token.php');

$action = $_REQUEST['action'];

switch($action){

  case "create":{

        $records = $_POST['records'];
        $data = json_decode(stripslashes($records));

        if(isset($_POST['cars'], $_POST['token'])){

          $cars = $data->{'cars'};

           if(Token::check($_POST['token'])){

                 $sqlQuery = "INSERT INTO the_cars (cars) VALUES (?)";

                if($statement = $con->prepare($sqlQuery)){
                    $statement->bind_param("s", $cars);
                    $statement->execute();
                    $success= true;
                }else{
                    $erro = $con->error;
                    $success = false;
                }
           }else{
               //error
           }

            echo json_encode(array(
                "success" => $sucess,
                'errors'=> $erro
            ));

            $statement->close();
            $conexao->close();

            break;
      }
    }
?>

I would appreciate help to understand in detail how to implement this type of protection, using the code above as an example.

Thanks in advance.

Some useful posts:

CSRF prevention for AJAX call from extjs to Struts action

How to implement CSRFGuard in ExtJs AjaxRequest?

ExtJS Store SYNC with Spring Security ON

http://blog.gugl.org/archives/category/extjs

EDITED

One possibility I like is to send the token on every Ajax request: https://www.sencha.com/forum/showthread.php?134125

Mabe in Aplication.js. file

init: function () {

 Ext.require(["Ext.util.Cookies", "Ext.Ajax"], function(){
    // Add csrf token to every ajax request
    var token = Ext.util.Cookies.get('csrftoken');
    if(!token){
        Ext.Error.raise("Missing csrftoken cookie");
    } else {
        Ext.Ajax.defaultHeaders = Ext.apply(Ext.Ajax.defaultHeaders || {}, {
            'X-CSRFToken': token
        });
    }
 });
}

OR from Building Applications with EXT JS video publish by PACKT, but with node in serverside

var csrfToken = Ext.query('meta[name=csrf-token]')[0].getAttribute('content');
Ext.Ajax.defaultHeaders = ('X-CSRF-Token': csrfToken);
Ext.Ajax.extraParams = {'csrf': csrfToken};

I still have doubts about how to properly relate the server side (generate the token and do the respective check) with the client side.

EDITED

I have made several attempts in the last few days to run the CSRFProtector with php and EXTJS.

From the analysis carried out I was able to verify the following with Chrome Dev Tools:

If just at de beginning of the file index I add (and not in the other php files):

include_once __DIR__ .'csrfp/libs/csrf/csrfprotector.php';
csrfProtector::init()

I get on Chrome Dev Tools:

csrfprotector.js file is loaded

In loaded php files I have » Method: POST, Status 200, Type xhr, Initiator csrfprotector.js:259

I see that the data (in JSON format) and a token are sent and Request Headers as a Cookie with the same token

In the index.php file, in addition, the following is created, as expected:

  (...)
  <script type="text/javascript" 
  src="http://my_path/csrfp/js/csrfprotector.js"></script>
  <script type="text/javascript">
  window.onload = function() {
      csrfprotector_init();
  };
 </script>
 </body>
 </html>

No error is returned

When I add at the beginning of the php file (containing the query that will receive the data of the request, for example to create a record) the include_one and csrfProtector::init() the request is made, success is false and I get a status code 403 and the message 403 Access Forbidden by CSRFProtector!

If I add an echo 'Test 1', before csrfProtector::init (); and an echo 'Test 2' after, just first echo works. So it's not a problem in my php code but in validation with csrfprotector.

In Dev Tools you see that error 403 is triggered by mentioning the following script line: csrfprotector: 259. line 259 of that file is: return this.old_send (data);

I'm going to explore the possible incompatibility of csrfprotector with JSON.

If we were able to run the CSRFProtector with PHP and EXTJS (with JSON), it would be a solution that could make all the difference for many, as it is very easy to implement.

Example of the data format received on the server-side:

Array
(
    [action] => create
    [_dc] => 1505398990654
    [data] => {"id_cars":"id_1","cars":"test"},
)
Tbar answered 1/9, 2017 at 20:21 Comment(6)
Updated my answer to show how to use CSRF-Protector from ExtJSCoenurus
Thanks Micael for your precious help and to have persisted in helping us find a solution with the CSRFProtector. I edited my fiddle with some data that can help identify the problem.Tbar
I know for sure Protector cant validate JSON requests (as mentioned in my answer) - sending JSON is the reason for status code 403. You can either switch to Content-type: application/x-www-form-urlencoded (example in linked Sencha Fiddle) or open the issue\help to fix Protector to make it work with json. The is no other way right now...Coenurus
Thanks Michal for your for your clarification and fantastic help.Tbar
You are welcome. I'v learned a lot by doing that ;)Coenurus
The newest release of CSRFProtector mentioned in the Michal answer note is apparently working fine with PHP and EXTJS. THanks again.Tbar
B
6

TL:DR

Considering you are using PHP, my main suggestion is to look at and use some existing solution like CSRF-Protector which is specifically designed to be used with PHP and should work with any client side framework including ExtJS. Its probably much better and safer than anything you can do by yourself.

NOTE: Project's wiki now contains two different pages with download links - this one contains links to outdated version and this one links to newest release. Make sure to download current release or clone repo!

Long Answer

I know your question is targeted on ExtJS solution specifically but it's too broad and missing some important details needed for good answer. What follows is some things you need to decide before you can even start thinking about "how to do this in code"...

Before i get into details, I strongly suggest to check following page for general considerations when designing CSRF protection: Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet

There are more ways how to handle CSRF protection. For the sake of simplicity I will discuss only the "Synchronizer (CSRF) Tokens" way described on the page mentioned above.

CSFR protection using "Synchronizer (CSRF) Tokens" always works like this:

  1. There is unprotected (in terms of CSRF) page\action\request which includes some form or action link which executes protected action (request). In your example it is the page which includes the ExtJs APP.MyApp class. This request also needs to generate CSRF token, store it in session (for later validation) and also include it somehow in generated response
  2. Protected action is requested while protection token is attached to the request
  3. Server-side action processing protected request extracts token from request and validates it against the value stored in session

Now there are more ways how to send generated CSRF tokens from server to client - meta, cookie, hidden field (all sort of mentioned in your question). The correct way to do it really depends on your app and desired protection level.

Main considerations are:

  • How the page initiating protected request is generated
  • What type of token are you using (per-session or per-request)

Token generation VS app\page lifestyle

As described in point 1. above, token is generated only when page initiating protected action is requested.

That's fine for multipage app because before protected action can be invoked, the page containing the form\link (and the token) must be generated. This means it's very easy to use per-request tokens and you can send the token in meta or form hidden field.

In the SPA app on the other side, where the initiating page is generated only once and protected action can be executed multiple times without full page refresh, your options are limited. Either you must use per-session token (see below) transferred via meta\header or you must use some more complicated mechanism to get new token using AJAX each time before you invoke protected action. For this case its better to use cookie as described in the link above it the "Double Submit Cookie" chapter

Token type

First you need to decide if your token will be per-session or per-request. Per-request tokens are generated for each request of the page initiating protected action and discarded after it is used (ie. protected action is executed after successful token validation). Can be stored in meta\header\hidden field. By definition, they are also not usable in SPA applications. There are also usability concerns like "back" button leading to false positive security event on the server if previous page is protected.

Per-session tokens are generated only once. This can lead to weaker security because it allows replay attacks but if your site is XSS safe (which it should be) its fine. Per-session tokens are easier to use in SPA applications. Can be transferred via meta\hidden field.

EDIT - CSRF-Protector VS ExtJs

It looks like current version of CSRF-Protector (v0.2.1) doesn't work with POST requests containing JSON payload (application/json) - see this issue in project's bug tracker. To work around this, make sure you always POST with Content-type: application/x-www-form-urlencoded.

For regular requests using Ext.Ajax.request do it by using params config instead of jsonData (fiddle)

Ext.Ajax.request({
            url: 'https://jsonplaceholder.typicode.com/posts/',
            method: 'POST',
            params: {
                "userId": 1,
                "id": 1,
                "title": "sunt",
                "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et"
            }
        })

For stores, make sure you configure writer with encode: true (see docs) like this (fiddle):

var store = new Ext.data.Store({
    model: 'MyApp.model.Post',
    storeId: 'postsStore',
    autoLoad: true,
    autoSync: true,
    loading: true,

    proxy: {
        type: 'rest',

        actionMethods: {
            create: 'POST',
            read: 'GET',
            update: 'PUT',
            destroy: 'DELETE'
        },

        api: {
            create: 'https://jsonplaceholder.typicode.com/posts',
            read: 'https://jsonplaceholder.typicode.com/posts',
            update: 'https://jsonplaceholder.typicode.com/posts',
            destroy: 'https://jsonplaceholder.typicode.com/posts'
        },

        reader: {
            type: 'json',
            rootProperty: 'data',
            totalProperty: 'total',
            successProperty: 'success'
        },

        writer: {
            type: 'json',
            writeAllFields: true,
            encode: true,
            rootProperty: 'data'
        }
    }
});
Barny answered 10/9, 2017 at 8:19 Comment(7)
Thanks Michal for the the very detailed answer. I'm looking into your suggestion to use a solution like CSRF-Protector. Apparently the settings are just made serverside, in my example case in PHP, with no need for any script in EXT JS (which facilitates implementation). This statement is correct, or on the contrary, client-side configurations (EXT JS) are required?Tbar
I am trying the CSRFProtector. I'm getting error 403. Maybe this error is because in extjs the data is sent to the server in JSON format and, as in the example, it is decoded in PHP with json_decode. Does anyone have an idea how to set up this library properly in this case.Tbar
Yes, from the Docs and code it seems only setup needed is server side. Client side should be transparent (php side injects its own js script into page which adds its own logic into XMLHttpRequest open\send methods - adding CSRF token to the request).Coenurus
If you are getting 403 it means your server-side is setup correctly and validation is failing. Json payload should not be a problem. Your code in query file is not hit because CSRF-Protector is intercepting incoming request (validation is done before your code is run). Few thing to look at: 1. Protector js script is included in your page 2.outgoing POST request contains CSRFP_TOKEN data (both by Dev tools in your browser). You can also check server side logs (by default in ../log/ dir relative to config file). Maybe update your question - add how your PHP file looks now...Coenurus
Update looks good. Not sure where the problem is. Maybe can you update how the actual request (the one failing with 403) looks like in Dev Tools ? I'm (un)fortunately not PHP guy so i cant try it myslef ;)Coenurus
Let us continue this discussion in chat.Coenurus
I agree. Thanks.Tbar

© 2022 - 2024 — McMap. All rights reserved.