【问题标题】:Handlebars: Access has been denied to resolve the property "from" because it is not an "own property" of its parentHandlebars:访问已被拒绝解析“来自”的属性,因为它不是其父级的“自己的属性”
【发布时间】:2020-04-28 15:35:15
【问题描述】:

我正在使用 Nodejs 后端和使用把手的服务器端渲染。 从车把读取doc 对象数组后,其中包含关键“内容”和“来自”。 但是,当我尝试使用 #each 循环遍历对象数组时, 出现错误“Handlebars: Access has been denied to resolve the property "from" because it is not an "own property" of its parent"。

我已经尝试 console.log() 我在 doc 数组中获取的数据,一切似乎都很好。

从某种角度来看,这是猫鼬查询,
我已将对象文档添加为 res.render 参数中的键。

Confession.find()
  .sort({date: -1})
  .then(function(doc){
    for(var i=0; i < doc.length; i++){
      //Check whether sender is anonymous
      if (doc[i].from === "" || doc[i].from == null){
        doc[i].from = "Anonymous";
      }

      //Add an extra JSON Field for formatted date
      doc[i].formattedDate = formatTime(doc[i].date);
    }
    res.render('index', {title: 'Confession Box', success:req.session.success, errors: req.session.errors, confession: doc});
    req.session.errors = null;
    req.session.success = null;
  });

这是我试图循环遍历的 .hbs 文件的一部分:

 {{#each confession}}
    <div class="uk-card uk-card-default uk-card-body uk-margin uk-align-center uk-width-1-2@m" >
        <div class="uk-text-bold">Message: </div>
        <div>{{this.content}}</div>
        <div>From: {{this.from}}</div>
        <div>Posted: {{this.formattedDate}}</div>
    </div>
    {{/each}}

【问题讨论】:

    标签: javascript node.js mongodb express handlebars.js


    【解决方案1】:

    尝试 npm install handlebars 4.5.3 版

    npm install handlebars@4.5.3

    它对我有用

    【讨论】:

    • 这应该是一条评论
    • 我目前使用的是 express-handlebars,版本 3.1.0
    • 谢谢,我都试过你的,@Mason 的回答器可以工作,但我不知道为什么会这样。
    【解决方案2】:

    我通过为车把安装开发依赖项解决了这个问题

    npm i -D handlebars@4.5.0

    【讨论】:

    • 哇这有效,为什么会发生这种情况?我目前正在使用 express-handlebars (3.1.0),我在我的 express 应用程序中将其设置为渲染引擎。
    • 由于某些限制,我怀疑这发生在较新版本的车把上,但我不知道如何处理这些限制。
    • 好吧,问题在于支持把手的 express 插件,但是一旦把手 4.5.0 保存为您的前端的主要引擎,请通过评论告诉我。
    • 这不起作用。执行 npm i -D handlebars@4.5.0 后仍然遇到同样的问题
    【解决方案3】:

    在最近发布的 Handlebars 中有一个 breaking change 导致了这个错误。

    您可以简单地添加他们在文档中建议的配置,但请注意,根据您的实施,这可能会导致漏洞受到 XXS 和 RCE 攻击。

    https://handlebarsjs.com/api-reference/runtime-options.html#options-to-control-prototype-access

    Confession.find()
      .sort({date: -1})
      .then(function(doc){
        for(var i=0; i < doc.length; i++){
          //Check whether sender is anonymous
          if (doc[i].from === "" || doc[i].from == null){
            doc[i].from = "Anonymous";
          }
    
          //Add an extra JSON Field for formatted date
          doc[i].formattedDate = formatTime(doc[i].date);
        }
        res.render('index', {title: 'Confession Box', success:req.session.success, errors: req.session.errors, confession: doc}, {
    
          // Options to allow access to the properties and methods which as causing the error.
    
          allowProtoMethodsByDefault: true,
          allowProtoPropertiesByDefault: true
    
        });
    
        req.session.errors = null;
        req.session.success = null;
      });
    

    【讨论】:

    • 啊,这就是我添加选项的地方,非常感谢!
    • 这对我不起作用。需要回调,而不是选项对象。
    【解决方案4】:

    今天我从车把收到同样的警告,但视图是空的。以下是我解决这个问题的方法:

    //  * USERS PAGE
    // @description        users route
    // @returns           ../views/users.hbs
    router.get('/users', async (req, res) => {
      // get all items from db collection
      const collection = 'User'
      await dbFindAllDocs(collection) // <=> wrapper for Model.find() ...
        .then(documents => {
          // create context Object with 'usersDocuments' key
          const context = {
            usersDocuments: documents.map(document => {
              return {
                name: document.name,
                location: document.location
              }
            })
          }
          // rendering usersDocuments from context Object
          res.render('users', {
            usersDocuments: context.usersDocuments
          })
        })
        .catch(error => res.status(500).send(error))
    })
    

    users.hbs 文件

    <ul>
    {{#each usersDocuments}}
    <li>name: {{this.name}} location: {{this.location}}</li>
    {{/each}}    
    </ul>
    

    使用自己的属性创建一个名为context 的全新对象,然后将其传递给渲染函数将解决问题...

    注意:

    当我们不创建新对象时,很容易意外暴露机密信息或可能危及项目安全性的信息,映射从数据库返回的数据并仅将需要的内容传递到视图可能会导致一个好习惯...

    【讨论】:

    • 非常感谢您的回答!创建一个新对象以防止不必要的数据泄露似乎更好。
    • 通过从准备好的列表中准备新的列表,它不会消耗 2 倍的时间来渲染吗?
    【解决方案5】:

    find() 返回的数据创建另一个新对象或数组将解决问题。看下面一个简单的插图

    app.get("/",(req,res)=>{
    
     let com = require('./MODELCOM')    // loading model
     let source=fs.readFileSync(__dirname+"/views/template.hbs","utf-8");
    
     com.find((err,data)=>{
        // creation new array  using map
       let wanted = data.map(doc=>{
           return {
               name:doc.name,
               _id:doc._id
            }
       })
    
        let html= handlebar.compile(source);
      fs.writeFileSync(__dirname+"/views/reciever.html",html({communities:wanted}))
        res.sendFile(__dirname+"/views/reciever.html")
    });
    

    【讨论】:

    • 与我的解决方案非常相似。就我而言,搜索返回的结果存在重大更改问题。我做了上面的地图转换。保持安全目标的最佳答案。
    【解决方案6】:

    如果使用 mongoose,这个问题可以通过使用 .lean() 获取 json 对象(而不是 mongoose 对象)来解决:

    dbName.find({}).lean()
      // execute query
      .exec(function(error, body) {
         //Some code
      });
    

    【讨论】:

    • 上帝保佑你!救生员!
    • 哇,这太棒了!
    • 谢谢。简单多了,解决了我的问题。
    • 谢谢,但您能解释一下问题的根源吗?
    • 是的......它正在工作......任何人请详细解释“lean()”功能
    【解决方案7】:

    “哇,这有效,为什么会发生这种情况?我目前正在使用我在 express 应用程序中设置为渲染引擎的 express-handlebars (3.1.0)。” – Lee Boon Kong 1 月 12 日 14:13

    “过去,Handlebars 允许您从模板访问输入对象的原型方法和属性...这种行为带来了多个安全问题...在 handlebars@^4.6.0.对对象原型的访问已被完全禁用。现在,如果您使用自定义类作为 Handlebars 的输入,您的代码将不再工作......这个包自动为每个模板调用添加运行时选项,禁用安全限制.. . 如果你的用户正在编写模板并且你在你的服务器上执行它们,你不应该使用这个包,而应该找到其他方法来解决这个问题......我建议你将你的类实例转换为普通的将 JavaScript 对象传递给模板函数之前。您访问的每个属性或函数都必须是其父级的“自己的属性”。 – 自述文件

    更多细节在这里: https://www.npmjs.com/package/@handlebars/allow-prototype-access

    快速而肮脏的不安全方法

    用法(express-handlebarsmongoose):

    express-handlebars 不允许您指定运行时选项以传递给模板函数。此软件包可帮助您禁用模型的原型检查。

    “仅当您完全控制在服务器中执行的模板时才这样做。”

    步骤:

    1 - 安装依赖项

    npm i @handlebars/allow-prototype-access

    2 - 以这个 sn-p 为例来重写你的 express 服务器

    const express = require('express');
    const mongoose = require('mongoose');
    const Handlebars = require('handlebars');
    const exphbs = require('express-handlebars');
    
    // Import function exported by newly installed node modules.
    const { allowInsecurePrototypeAccess } = require('@handlebars/allow-prototype-access');
    
    const PORT = process.env.PORT || 3000;
    
    const app = express();
    
    const routes = require('./routes');
    
    app.use(express.urlencoded({ extended: true }));
    app.use(express.json());
    app.use(express.static('public'));
    
    // When connecting Handlebars to the Express app...
    app.engine('handlebars', exphbs({
        defaultLayout: 'main',
        // ...implement newly added insecure prototype access
        handlebars: allowInsecurePrototypeAccess(Handlebars)
        })
    );
    app.set('view engine', 'handlebars');
    
    app.use(routes);
    
    const MONGODB_URI = process.env.MONGODB_URI || >'mongodb://localhost/dbName';
    
    mongoose.connect(MONGODB_URI);
    
    app.listen(PORT, function () {
      console.log('Listening on port: ' + PORT);
    });
    

    3 - 运行服务器并跳起快乐的舞蹈。


    更长更安全的方法

    在将 AJAX 调用返回的对象传递给 Handlebars 模板之前,将其映射到一个新对象,其中包含您需要在 .hbs 文件中访问的每个属性或函数。 您可以在下面看到在将其传递给 Handlebars 模板之前创建的新对象。

    const router = require("express").Router();
    const db = require("../../models");
    
    router.get("/", function (req, res) {
        db.Article.find({ saved: false })
            .sort({ date: -1 })
            .then(oldArticleObject => {
                const newArticleObject = {
                    articles: oldArticleObject.map(data => {
                        return {
                            headline: data.headline,
                            summary: data.summary,
                            url: data.url,
                            date: data.date,
                            saved: data.saved
                        }
                    })
                }
                res.render("home", {
                    articles: newArticleObject.articles
                })
            })
            .catch(error => res.status(500).send(error));
    });
    

    你的猫鼬查询

    如果我错了,请纠正我,但我认为这可能适用于您的查询...

    Confession.find()
        .sort({ date: -1 })
        .then(function (oldDoc) {
    
            for (var i = 0; i < oldDoc.length; i++) {
                //Check whether sender is anonymous
                if (oldDoc[i].from === "" || oldDoc[i].from == null) {
                    oldDoc[i].from = "Anonymous";
                }
    
                //Add an extra JSON Field for formatted date
                oldDoc[i].formattedDate = formatTime(oldDoc[i].date);
            }
    
            const newDoc = {
                doc: oldDoc.map(function (data) {
                    return {
                        from: data.from,
                        formattedDate: data.formattedDate
                    }
                })
            }
            
            res.render('index', { title: 'Confession Box', success: req.session.success, errors: req.session.errors, confession: newDoc.doc });
            req.session.errors = null;
            req.session.success = null;
        });
    

    【讨论】:

    • 精美细节。非常感谢!
    • 感谢这个伟大的解决方案!它对我有用。
    【解决方案8】:

    从 4.6.0 版本开始,Handlebars 默认禁止访问上下文对象的原型属性和方法。这与此处描述的安全问题有关:https://mahmoudsec.blogspot.com/2019/04/handlebars-template-injection-and-rce.html

    参考https://github.com/wycats/handlebars.js/issues/1642

    如果您确定只有开发人员可以访问模板,则可以通过安装以下包来允许原型访问:

    npm i @handlebars/allow-prototype-access
    

    如果您使用的是 express-handlebars,您应该按照以下方式进行:

    const 
        express = require('express'),
        _handlebars = require('handlebars'),
        expressHandlebars = require('express-handlebars'),
        {allowInsecurePrototypeAccess} = require('@handlebars/allow-prototype-access')
    
    const app = express()
    
    app.engine('handlebars', expressHandlebars({
        handlebars: allowInsecurePrototypeAccess(_handlebars)
    }))
    app.set('view engine', 'handlebars')
    

    【讨论】:

    • 感谢这工作。所以每次我们必须使用快速车把时,我们都必须这样做?
    【解决方案9】:

    我正在使用Angular version 8.0.2Node version 10.16.3 运行测试用例时遇到以下问题:

    Handlebars:已拒绝访问以解决该属性 “语句”,因为它不是其父级的“自己的财产”。

    Handlebars:已拒绝访问以解析属性“功能” 因为它不是其父级的“自己的财产”。

    在调试问题时进一步发现package.json, "karma-coverage-istanbul-reporter": "2.0.1" 存在,但"istanbul-lib-report" 缺失,因此执行以下步骤:

    1. 在 package.json 文件中,依赖项下包含“istanbul-lib-report”:“3.0.0”
    2. 执行 npm 安装

    它解决了我的问题:) (希望这对某人有所帮助)

    【讨论】:

    【解决方案10】:

    解决此问题的一种更简洁的方法是使用 mongoose 文档的 .toJSON() 方法。

    let data = dbName.find({})
      .exec(function(error, body) {
         //Some code
      });
        data = data.toJSON()
    //use {{data}} on .hbs template
    

    【讨论】:

    • 这是最适合我的方法,无需使用 npm 更改或添加任何内容。
    【解决方案11】:

    有一个适用于所有版本的 hbs 的解决方法: 这样做并将数据库发送到页面。这可以在不更改 Handlbar 模板的情况下工作,我们最终可以处理 0 个漏洞

    var database=[];
    for(var i=0;i<foundData.length;i++)
    {
     database[i]=foundData[i].toObject();
    }
    

    【讨论】:

      【解决方案12】:

      我添加了一个地图功能,它对我有用:

          Confession.find()
            .sort({date: -1})
            .then(function(doc){
              for(var i=0; i < doc.length; i++){
                //Check whether sender is anonymous
                if (doc[i].from === "" || doc[i].from == null){
                  doc[i].from = "Anonymous";
                }
          
                //Add an extra JSON Field for formatted date
                doc[i].formattedDate = formatTime(doc[i].date);
              }
              res.render('index', {title: 'Confession Box', success:req.session.success, errors: req.session.errors, confession: **doc.map(doc=>doc.toJSON())}**);
              req.session.errors = null;
              req.session.success = null;
            });
      
      

      【讨论】:

      • 你能再解释一下你的代码吗?谢谢!
      • 是的,这是重要的部分(忏悔:doc.map(doc=>doc.toJSON())})基本上只是将先前的答案转换为数组,这应该可以解决
      【解决方案13】:

      只需添加以下代码即可解决问题..... 在使用该安装之前,请通过以下命令允许原型。如果您有任何问题评论:...

      安装模块

      npm install @handlebars/allow-prototype-access
      

      导入包

      const Handlebars = require('handlebars')
      const {allowInsecurePrototypeAccess} = require('@handlebars/allow-prototype- 
      access')
      

      设置视图引擎

      app.engine('handlebars', expressHandlebars({
          handlebars: allowInsecurePrototypeAccess(Handlebars)
      }));  
      app.set('view engine', 'handlebars');
      ...
      

      【讨论】:

        【解决方案14】:

        我使用猫鼬,通过这个功能可以解决你的问题。

        const confession=await Confession.find({}).lean();
        res.render('index',{confession});
        

        【讨论】:

          猜你喜欢
          • 2020-10-06
          • 2021-05-09
          • 2020-10-04
          • 2020-06-12
          • 2023-03-18
          • 2020-08-16
          • 2012-08-31
          • 2012-11-16
          相关资源
          最近更新 更多