Handlebars:已拒绝访问以解析 属性 "from",因为它不是其父项的 "own property"

Handlebars: Access has been denied to resolve the property "from" because it is not an "own property" of its parent

我正在使用 Nodejs 后端和使用把手的服务器端渲染。 从车把中读取 doc 个对象数组后,其中包含键 "content" 和 "from"。 但是,当我尝试使用 #each 遍历对象数组时, 出现错误“Handlebars:已拒绝访问以解析 属性 "from",因为它不是其父项的 "own property"”。

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

从某些角度来看,这是 mongoose 查询,
我已将对象文档添加为 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}}

试试 npm install handlebars 版本 4.5.3

npm 安装 handlebars@4.5.3

对我有用

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

npm i -D handlebars@4.5.0

最近发布的 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;
  });

今天我从 handlebars 收到了同样的警告,而且视图是空的。以下是我如何修复的:

//  * 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 的全新对象,然后将其传递给渲染函数将解决此问题...

注:

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

根据 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")
});

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

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

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

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

这里有更多详细信息: https://www.npmjs.com/package/@handlebars/allow-prototype-access

QUICK AND DIRTY INSECURE METHOD

Usage (express-handlebars and mongoose):

express-handlebars does not allow you to specify runtime-options to pass to the template function. This package can help you disable prototype checks for your models.

"Only do this, if you have full control over the templates that are executed in the server."

Steps:

1 - Install dependency

npm i @handlebars/allow-prototype-access

2 - Use this snippet as an example to rewrite your express server

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 - Run the server and do your happy dance.


更长更安全的方法

在将您的 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;
    });

从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

如果您使用的是快速车把,您应该按以下步骤操作:

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')

我正在使用 Angular version 8.0.2Node version 10.16.3 而 运行 测试用例面临以下问题:

Handlebars: Access has been denied to resolve the property "statements" because it is not an "own property" of its parent.

Handlebars: Access has been denied to resolve the property "functions" because it is not an "own property" of its parent.

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

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

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

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

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

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

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

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

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

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

安装模块

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');
...

我用的是mongoose,这个功能可以解决你的问题。

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