【问题标题】:EXTJS CSRF protectionEXTJS CSRF 保护
【发布时间】:2018-02-10 22:44:48
【问题描述】:

我正在尝试在一个应用中实施针对 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 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

已编辑

我喜欢的一种可能性是在每个 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
        });
    }
 });
}

OR 来自 使用 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};

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

已编辑

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

通过进行的分析,我能够使用 Chrome 开发工具验证以下内容:

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

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

我开始使用 Chrome 开发工具:

csrfprotector.js 文件已加载

在我加载的 php 文件中 » 方法:POST,Status 200,Type xhr,Initiator 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 CSRFProtector 禁止访问!

如果我在 csrfProtector::init (); 之前添加一个 echo 'Test 1',和一个 echo 'Test 2' 之后,只是第一个 echo 有效。所以这在我的 php 代码中不是问题,而是在使用 csrfprotector 进行验证时。

在开发工具中,您会看到错误 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"},
)

【问题讨论】:

  • 更新了我的答案以展示如何使用 ExtJS 中的 CSRF-Protector
  • 感谢 Micael 的宝贵帮助,并坚持帮助我们找到 CSRFProtector 的解决方案。我用一些可以帮助识别问题的数据编辑了我的小提琴。
  • 我确定 Protector 无法验证 JSON 请求(如我的回答中所述) - 发送 JSON 是状态码 403 的原因。您可以切换到 Content-type: application/x-www-form-urlencoded(链接 Sencha Fiddle 中的示例)或打开问题\帮助来修复保护器以使其与 json 一起使用。目前没有其他办法......
  • 感谢 Michal 的澄清和出色的帮助。
  • 不客气。通过这样做,我学到了很多东西;)

标签: javascript php extjs extjs5 csrf-protection


【解决方案1】:

TL:DR

考虑到您使用的是 PHP,我的主要建议是查看并使用一些现有的解决方案,例如 CSRF-Protector,它专门设计用于 PHP,并且应该适用于包括 ExtJS 在内的任何客户端框架。它可能比您自己做的任何事情都更好,更安全。

注意:Project 的 wiki 现在包含两个带有下载链接的不同页面 - this one 包含指向过时版本的链接,this one 包含指向最新版本的链接。确保下载当前版本或克隆 repo!

长答案

我知道您的问题是专门针对 ExtJS 解决方案的,但它过于宽泛,并且缺少一些重要的细节来获得好的答案。接下来是一些你需要决定的事情,然后你才能开始思考“如何在代码中做到这一点”......

在详细介绍之前,我强烈建议您在设计 CSRF 保护时查看以下页面以了解一般注意事项:Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet

有更多方法可以处理 CSRF 保护。为了简单起见,我将只讨论上面提到的页面上描述的“Synchronizer (CSRF) Tokens”方式。

使用“同步器 (CSRF) 令牌”的 CSFR 保护总是这样工作:

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

现在有更多方法可以将生成的 CSRF 令牌从服务器发送到客户端 - 元、cookie、隐藏字段(您的问题中提到了所有这些)。执行此操作的正确方法实际上取决于您的应用和所需的保护级别。

主要考虑因素是:

  • 如何生成发起受保护请求的页面
  • 您使用的是什么类型的令牌(按会话或按请求)

令牌生成 VS 应用\页面生活方式

如上面第 1 点所述,仅当请求页面启动受保护操作时才会生成令牌。

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

在另一边的 SPA 应用程序中,启动页面只生成一次,受保护的操作可以多次执行而无需刷新整个页面,您的选择是有限的。您必须使用通过 meta\header 传输的每个会话令牌(见下文),或者您必须使用一些更复杂的机制在每次调用受保护操作之前使用 AJAX 获取新令牌。对于这种情况,最好使用上面“双重提交 Cookie”章节中描述的 cookie

令牌类型

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

每个会话令牌仅生成一次。 这可能会导致安全性变弱,因为它允许重放攻击,但如果您的站点是 XSS 安全的(应该是安全的),那就没问题了。每会话令牌更易于在 SPA 应用程序中使用。可以通过元\隐藏字段传输。

编辑 - CSRF-Protector VS ExtJs

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

对于使用 Ext.Ajax.request 的常规请求,请使用 params 配置而不是 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 配置编写器(请参阅docs),如下所示(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'
        }
    }
});

【讨论】:

  • 感谢 Michal 提供非常详细的答案。我正在研究您使用 CSRF-Protector 之类的解决方案的建议。显然,这些设置只是在服务器端进行的,在我的 PHP 示例中,不需要 EXT JS 中的任何脚本(这有助于实现)。这种说法是正确的,还是相反,需要客户端配置(EXT JS)?
  • 我正在尝试 CSRFProtector。我收到错误 403。也许这个错误是因为在 extjs 中,数据以 JSON 格式发送到服务器,并且如示例中所示,它在 PHP 中使用 json_decode 进行解码。有谁知道在这种情况下如何正确设置这个库。
  • 是的,从文档和代码看来,服务器端似乎只需要设置。客户端应该是透明的(php 端将自己的 js 脚本注入页面,将自己的逻辑添加到 XMLHttpRequest open\send 方法中 - 将 CSRF 令牌添加到请求中)。
  • 如果您收到 403,则表示您的服务器端设置正确且验证失败。 Json 有效载荷应该不是问题。 query 文件中的代码未命中,因为 CSRF-Protector 正在拦截传入请求(在运行代码之前完成验证)。有几件事要看: 1. Protector js 脚本包含在您的页面中 2. 发出的 POST 请求包含 CSRFP_TOKEN 数据(均由浏览器中的开发工具提供)。您还可以检查服务器端日志(默认情况下在 ../log/ 相对于配置文件的目录中)。也许更新您的问题 - 添加您的 PHP 文件现在的外观...
  • 更新看起来不错。不确定问题出在哪里。也许您可以更新实际请求(以 403 失败的请求)在 Dev Tools 中的样子吗?幸运的是,我不是 PHP 人,所以我不能自己尝试 ;)
猜你喜欢
  • 1970-01-01
  • 2020-12-18
  • 2018-05-27
  • 2012-10-08
  • 1970-01-01
  • 1970-01-01
  • 2011-10-07
  • 2019-02-03
  • 2016-01-12
相关资源
最近更新 更多