EXTJS CSRF-защита

Я пытаюсь реализовать защиту в одном приложении от CSRF.

В PHP это относительно просто реализовать. У меня много вопросов о том, как это сделать с Extjs.

Книги по EXTJS, которые я читал, не затрагивают эту тему, и я не могу найти конкретных руководств по этому вопросу — с EXTJS — в Интернете.

Некоторые вопросы:

Используя PHP, токен отправляется в EXTJS?

Должен ли я создавать скрытое поле в каждой форме, как в PHP?

Должен ли я отправлять на сервер токен в Ext.Ajax.requestt? Как это сделать?

Некоторый очень простой код в качестве отправной точки:

Токен класса: 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;
 }
}
?>

Запрос

<?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;
      }
    }
?>

Я был бы признателен за помощь в подробном понимании того, как реализовать этот тип защиты, используя приведенный выше код в качестве примера.

Заранее спасибо.

Несколько полезных постов:

Предотвращение CSRF для вызова AJAX из extjs в действие Struts

Как реализовать CSRFGuard в ExtJs AjaxRequest?

ExtJS Store SYNC с включенной Spring Security

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

ОТРЕДАКТИРОВАНО

Мне нравится одна из возможностей — отправлять токен при каждом запросе Ajax: https://www.sencha.com/forum/showthread.php?134125

Мейб в Aplication.js. файл

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
        });
    }
 });
}

ИЛИ из видео Building Applications with EXT JS, опубликованного PACKT, но с узлом на стороне сервера

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

У меня все еще есть сомнения относительно того, как правильно связать серверную часть (сгенерировать токен и выполнить соответствующую проверку) с клиентской стороной.

ОТРЕДАКТИРОВАНО

За последние несколько дней я предпринял несколько попыток запустить CSRFProtector с php и EXTJS.

В результате проведенного анализа с помощью Chrome Dev Tools я смог проверить следующее:

Если я добавляю только в начале индекса файла (а не в других файлах php):

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

Я получаю инструменты Chrome Dev:

файл csrfprotector.js загружен

В загруженных файлах php у меня есть »Метод: POST, статус 200, тип xhr, инициатор csrfprotector.js: 259

Я вижу, что данные (в формате JSON) и токен отправляются, а заголовки запрашиваются как файл cookie с тем же токеном.

Кроме того, в файле index.php, как и ожидалось, создается следующее:

  (...)
  <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>

Ошибка не возвращается

Когда я добавляю в начало файла php (содержащего запрос, который будет получать данные запроса, например, для создания записи) include_one и csrfProtector::init(), запрос выполняется, успех равен false, и я получаю код состояния 403 и сообщение 403 Access Forbidden by CSRFProtector!

Если я добавлю эхо «Тест 1», перед csrfProtector::init(); и эхо «Тест 2» после этого работает только первое эхо. Так что проблема не в моем php-коде, а в проверке с помощью csrfprotector.

В Dev Tools вы видите, что ошибка 403 возникает при упоминании следующей строки скрипта: csrfprotector: 259. Строка 259 этого файла: return this.old_send (data);

Я собираюсь исследовать возможную несовместимость csrfprotector с JSON.

Если бы мы могли запустить CSRFProtector с PHP и EXTJS (с JSON), это было бы решение, которое могло бы иметь большое значение для многих, поскольку его очень легко реализовать.

Пример формата данных, полученных на стороне сервера:

Array
(
    [action] => create
    [_dc] => 1505398990654
    [data] => {"id_cars":"id_1","cars":"test"},
)

person josei    schedule 01.09.2017    source источник
comment
Обновлен мой ответ, чтобы показать, как использовать CSRF-Protector из ExtJS.   -  person Michal Levý    schedule 13.09.2017
comment
Спасибо Микаэлю за вашу неоценимую помощь и за то, что он настойчиво помогает нам найти решение с помощью CSRFProtector. Я отредактировал свою скрипку с некоторыми данными, которые могут помочь определить проблему.   -  person josei    schedule 13.09.2017
comment
Я точно знаю, что Protector не может проверять запросы JSON (как упоминалось в моем ответе) - отправка JSON является причиной кода состояния 403. Вы можете либо переключиться на Content-type: application/x-www-form-urlencoded (пример в связанной Sencha Fiddle), либо открыть вопрос\помощь, чтобы исправить Protector на заставить его работать с json. Другого пути сейчас нет...   -  person Michal Levý    schedule 13.09.2017
comment
Спасибо, Михал, за разъяснения и фантастическую помощь.   -  person josei    schedule 13.09.2017
comment
Пожалуйста. Я многому научился, делая это ;)   -  person Michal Levý    schedule 13.09.2017
comment
Новейшая версия CSRFProtector, упомянутая в примечании к ответу Михала, по-видимому, отлично работает с PHP и EXTJS. Еще раз спасибо.   -  person josei    schedule 14.09.2017


Ответы (1)


TL:DR

Учитывая, что вы используете PHP, мое главное предложение — посмотреть и использовать какое-нибудь существующее решение, такое как CSRF -Protector, который специально разработан для использования с PHP и должен работать с любым клиентским фреймворком, включая ExtJS. Это, вероятно, намного лучше и безопаснее, чем все, что вы можете сделать самостоятельно.

ПРИМЕЧАНИЕ. Вики проекта теперь содержит две разные страницы со ссылками для скачивания: web-application" rel="nofollow noreferrer">это содержит ссылки на устаревшую версию и здесь ссылка на последнюю версию. Обязательно загрузите текущую версию или клонируйте репозиторий!

Длинный ответ

Я знаю, что ваш вопрос нацелен конкретно на решение ExtJS, но он слишком широк и в нем отсутствуют некоторые важные детали, необходимые для хорошего ответа. Ниже приведены некоторые вещи, которые вам нужно решить, прежде чем вы сможете даже начать думать о том, как сделать это в коде...

Прежде чем я углублюсь в детали, я настоятельно рекомендую ознакомиться со следующей страницей для общих соображений при разработке защиты от CSRF: Памятка по предотвращению подделки межсайтовых запросов (CSRF)

Есть и другие способы защиты от CSRF. Для простоты я буду обсуждать только токен Synchronizer (CSRF), описанный на странице, упомянутой выше.

Защита CSFR с использованием токенов Synchronizer (CSRF) всегда работает следующим образом:

  1. Существует незащищенная (с точки зрения CSRF) страница\действие\запрос, которая включает в себя какую-то форму или ссылку на действие, которое выполняет защищенное действие (запрос). В вашем примере это страница, которая включает класс ExtJs APP.MyApp. Этот запрос также должен сгенерировать токен CSRF, сохранить его в сеансе (для последующей проверки), а также каким-то образом включить его в сгенерированный ответ.
  2. Запрошено защищенное действие, пока к запросу прикреплен токен защиты
  3. Действие на стороне сервера, обрабатывающее защищенный запрос, извлекает токен из запроса и проверяет его на соответствие значению, хранящемуся в сеансе.

Теперь есть больше способов отправки сгенерированных токенов CSRF с сервера на клиент - мета, cookie, скрытое поле (все упомянутые в вашем вопросе). Правильный способ сделать это действительно зависит от вашего приложения и желаемого уровня защиты.

Основные соображения:

  • Как генерируется страница, инициирующая защищенный запрос
  • Какой тип токена вы используете (для сеанса или для каждого запроса)

Генерация токенов VS образ жизни приложения\страницы

Как описано в пункте 1 выше, токен генерируется только тогда, когда запрашивается страница, инициирующая защищенное действие.

Это нормально для многостраничного приложения, потому что перед тем, как можно будет вызвать защищенное действие, должна быть сгенерирована страница, содержащая форму\ссылку (и токен). Это означает, что очень легко использовать токены для каждого запроса, и вы можете отправить токен в мета или сформировать скрытое поле.

С другой стороны, в приложении SPA, где инициирующая страница создается только один раз, а защищенное действие может выполняться несколько раз без полного обновления страницы, ваши возможности ограничены. Либо вы должны использовать токен для каждого сеанса (см. ниже), передаваемый через meta\header, либо вы должны использовать какой-то более сложный механизм для получения нового токена с помощью AJAX каждый раз перед вызовом защищенного действия. В этом случае лучше использовать куки, как описано в ссылке выше, в главе «Двойная отправка куки».

Тип токена

Сначала вам нужно решить, будет ли ваш токен для сеанса или для каждого запроса. Токены для каждого запроса генерируются для каждого запроса страницы, инициирующей защищенное действие, и отбрасываются после его использования (т. е. защищенное действие выполняется после успешной проверки токена). Может храниться в мета\заголовке\скрытом поле. По определению они также не могут использоваться в приложениях SPA. Существуют также проблемы с удобством использования, такие как кнопка «Назад», приводящая к ложному положительному событию безопасности на сервере, если предыдущая страница защищена.

Токены сеанса генерируются только один раз. Это может привести к снижению безопасности, поскольку допускает повторные атаки, но если ваш сайт защищен от XSS (что и должно быть), все в порядке. Сеансовые токены легче использовать в приложениях SPA. Можно передать через мета\скрытое поле.

РЕДАКТИРОВАТЬ - CSRF-протектор VS ExtJs

Похоже, что текущая версия CSRF-Protector (v0.2.1) не работает с запросами POST, содержащими полезную нагрузку JSON (application/json) — см. эта проблема в системе отслеживания ошибок проекта. Чтобы обойти это, убедитесь, что вы всегда выполняете POST с Content-type: application/x-www-form-urlencoded.

Для обычных запросов с использованием Ext.Ajax.request используйте params config вместо 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"
            }
        })

Для хранилищ убедитесь, что вы настроили средство записи с encode: true (см. ="nofollow noreferrer">документы), как это (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'
        }
    }
});
person Michal Levý    schedule 10.09.2017
comment
Спасибо Михал за очень подробный ответ. Я рассматриваю ваше предложение использовать такое решение, как CSRF-Protector. По-видимому, настройки просто выполняются на стороне сервера, в моем примере на PHP, без необходимости в каком-либо сценарии в EXT JS (что облегчает реализацию). Это утверждение верно, или наоборот требуются конфигурации на стороне клиента (EXT JS)? - person josei; 10.09.2017
comment
Пробую CSRFProtector. Я получаю ошибку 403. Возможно, эта ошибка связана с тем, что в extjs данные отправляются на сервер в формате JSON и, как в примере, они декодируются в PHP с помощью json_decode. Кто-нибудь знает, как правильно настроить эту библиотеку в этом случае. - person josei; 11.09.2017
comment
Да, из документов и кода кажется, что необходима только настройка на стороне сервера. Клиентская сторона должна быть прозрачной (сторона php внедряет свой собственный js-скрипт на страницу, который добавляет свою логику в методы открытия\отправки XMLHttpRequest — добавление токена CSRF к запросу). - person Michal Levý; 11.09.2017
comment
Если вы получаете 403, это означает, что ваша серверная часть настроена правильно и проверка не удалась. Полезная нагрузка Json не должна быть проблемой. Ваш код в файле query не срабатывает, потому что CSRF-Protector перехватывает входящий запрос (проверка выполняется до запуска вашего кода). Несколько вещей, на которые стоит обратить внимание: 1. Сценарий Protector js включен в вашу страницу 2. Исходящий запрос POST содержит данные CSRFP_TOKEN (оба с помощью инструментов Dev в вашем браузере). Вы также можете проверить журналы на стороне сервера (по умолчанию в каталоге ../log/ относительно файла конфигурации). Возможно, обновите свой вопрос - добавьте, как сейчас выглядит ваш файл PHP... - person Michal Levý; 11.09.2017
comment
Обновление выглядит хорошо. Не уверен, в чем проблема. Может быть, вы можете обновить, как фактический запрос (тот, который не работает с 403) выглядит в Dev Tools? Я (не)к счастью, не PHP-парень, поэтому я не могу попробовать это сам;) - person Michal Levý; 11.09.2017
comment
Давайте продолжим обсуждение в чате. - person Michal Levý; 14.09.2017
comment
Я согласен. Спасибо. - person josei; 14.09.2017