EXTJS CSRF保护

jos*_*sei 10 javascript php extjs csrf-protection extjs5

我试图在一个应用程序中实施针对CSRF的保护.

在PHP中,它实现起来相对简单.关于如何使用Extjs,我有很多问题.

我读过的EXTJS书籍没有涉及这个主题,我无法在互联网上找到关于这个主题的具体指导 - 使用EXTJS.

一些问题:

使用PHP,令牌被发送到EXTJS?

我是否必须像PHP一样在每个表单中创建一个隐藏字段?

我是否必须在Ext.Ajax.requestt中向服务器端发送令牌?这该怎么做?

一些非常简单的代码作为起点:

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;
 }
}
?>
Run Code Online (Sandbox Code Playgroud)

询问

<?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;
      }
    }
?>
Run Code Online (Sandbox Code Playgroud)

我将非常感谢帮助,以上面的代码为例,详细了解如何实现这种类型的保护.

提前致谢.

一些有用的帖子:

从extjs到Struts动作的AJAX调用的CSRF预防

如何在ExtJs AjaxRequest中实现CSRFGuard?

具有Spring Security ON的ExtJS Store SYNC

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

EDITED

我喜欢的一种可能性是在每个Ajax请求上发送令牌:https://www.sencha.com/forum/showthread.php?134125

Aplication.js中的Mabe.文件

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
        });
    }
 });
}
Run Code Online (Sandbox Code Playgroud)

或者来自构建应用程序,使用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};
Run Code Online (Sandbox Code Playgroud)

我仍然怀疑如何正确地将服务器端(生成令牌并进行相应的检查)与客户端相关联.

EDITED

在过去的几天里,我已经多次尝试使用php和EXTJS运行CSRFProtector.

从进行的分析中,我能够使用Chrome Dev Tools验证以下内容:

如果只是在文件索引的开头我添加(而不是在其他php文件中):

include_once __DIR__ .'csrfp/libs/csrf/csrfprotector.php';
csrfProtector::init()
Run Code Online (Sandbox Code Playgroud)

我上了Chrome开发工具:

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>
Run Code Online (Sandbox Code Playgroud)

没有错误返回

当我在php文件的开头添加(包含将接收请求数据的查询,例如创建记录)时,include_one和csrfProtector :: init()发出请求,成功为false,我得到CSRFProtector的状态码403和消息403禁止访问!

如果我在csrfProtector :: init()之前添加echo'Test 1'; 然后回声"测试2",只是第一次回声起作用.所以这不是我的PHP代码中的问题,而是使用csrfprotector进行验证.

在Dev Tools中,您会看到错误403是通过提及以下脚本行来触发的:csrfprotector:259.该文件的第259行是:return this.old_send(data);

我将探讨csrfprotector与JSON可能不兼容的问题.

如果我们能够使用PHP和EXTJS(使用JSON)运行CSRFProtector,那么它将是一个可以为许多人带来巨大变化的解决方案,因为它非常容易实现.

服务器端收到的数据格式示例:

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

Mic*_*evý 5

TL:DR

您使用的PHP综合考虑,我的主要建议是看和使用像一些现有的解决方案CSRF保护器,其专门设计用于与PHP一起使用,并应与任何客户端框架,包括ExtJS的工作.它可能比你自己做的更好,更安全.

注意:Project的wiki现在包含两个带有下载链接的不同页面 - 这个包含过时版本的链接,这个链接到最新版本.确保下载当前版本或克隆回购!

答案很长

我知道你的问题专门针对ExtJS解决方案,但它太宽泛,缺少一些重要的细节,需要良好的答案.以下是在您开始考虑"如何在代码中执行此操作"之前需要确定的一些事项...

在我详细介绍之前,我强烈建议在设计CSRF保护时查看以下页面以了解一般注意事项:跨站点请求伪造(CSRF)预防备忘单

有多种方法可以处理CSRF保护.为简单起见,我将仅讨论上述页面中描述的"同步器(CSRF)令牌"方式.

使用"Synchronizer(CSRF)Tokens"的CSFR保护始终如下:

  1. 有未受保护的(就CSRF而言)page\action\request,其中包含执行受保护操作(请求)的某种表单或操作链接.在您的示例中,它是包含ExtJs APP.MyApp类的页面.此请求还需要生成CSRF令牌,将其存储在会话中(以供稍后验证),并在生成的响应中以某种方式包含它
  2. 请求受保护的操作,同时将保护令牌附加到请求
  3. 服务器端操作处理受保护请求从请求中提取令牌,并根据会话中存储的值对其进行验证

现在有更多方法可以将生成的CSRF令牌从服务器发送到客户端 - 元,cookie,隐藏字段(在您的问题中提到的所有类型).正确的方法取决于您的应用程序和所需的保护级别.

主要考虑因素是:

  • 如何生成启动受保护请求的页面
  • 您使用什么类型的令牌(每个会话或每个请求)

令牌生成VS app\page生活方式

如上面第1点所述,只有在请求页面启动受保护的操作时才会生成令牌.

这对于多页应用程序来说很好,因为在调用受保护的操作之前,必须生成包含表单\ link(和令牌)的页面.这意味着使用每个请求令牌非常容易,您可以在元或表单隐藏字段中发送令牌.

在另一侧的SPA应用程序中,启动页面仅生成一次且受保护的操作可以多次执行而无需完整页面刷新,因此您的选项有限.要么必须使用通过meta\header传输的每会话令牌(见下文),要么每次调用受保护的操作之前,必须使用一些更复杂的机制来使用AJAX获取新令牌.对于这种情况,最好使用cookie,如上面链接中所述的"Double Submit Cookie"一章

令牌类型

首先,您需要确定您的令牌是按会话还是按请求.针对发起受保护动作的页面的每个请求生成每请求令牌,并在使用它之后将其丢弃(即,在成功令牌验证之后执行受保护动作).可以存储在meta\header\hidden字段中.根据定义,它们也不适用于SPA应用程序.如果前一页受到保护,还有"后退"按钮等可用性问题会导致服务器上的误报安全事件.

每会话令牌仅生成一次.这可能导致较弱的安全性,因为它允许重放攻击,但如果您的站点是XSS安全(它应该是)它的罚款.每个会话令牌在SPA应用程序中更易于使用.可以通过meta\hidden字段传输.

编辑 - CSRF-Protector VS ExtJs

看起来当前版本的CSRF-Protector(v0.2.1)不适用于包含JSON payload(application/json)的POST请求- 请参阅项目的bug跟踪器中的此问题.要解决此问题,请确保始终使用POST Content-type: application/x-www-form-urlencoded.

对于使用常规的请求Ext.Ajax.request通过做params配置,而不是jsonData(小提琴)

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"
            }
        })
Run Code Online (Sandbox Code Playgroud)

对于商店,请确保您使用encode: true(参见文档)这样配置编写器(小提琴):

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'
        }
    }
});
Run Code Online (Sandbox Code Playgroud)