Handlebars:访问已被拒绝解析“来自”的属性,因为它不是其父级的“自己的属性”

Lee*_*ong 43 javascript mongodb node.js express handlebars.js

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

我尝试 console.log() 我在文档数组中获取的数据,一切看起来都很好。

从某些角度来看,这是猫鼬查询,
我已将对象文档添加为 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;
  });
Run Code Online (Sandbox Code Playgroud)

这是我尝试循环遍历的 .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}}
Run Code Online (Sandbox Code Playgroud)

Bil*_*leh 64

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

dbName.find({}).lean()
  // execute query
  .exec(function(error, body) {
     //Some code
  });
Run Code Online (Sandbox Code Playgroud)

  • 上帝祝福你!救生员! (7认同)
  • 哇,这太棒了! (3认同)
  • 谢谢。如此简单并解决了我的问题。 (3认同)
  • 谢谢,但是您向我们解释一下问题的根源吗? (2认同)
  • 是的...它正在工作..任何人请详细解释“lean()”函数 (2认同)
  • 来自 mongoose.js.com :“精益选项告诉 Mongoose 跳过对结果文档进行水合处理。这使得查询速度更快,内存占用更少,但结果文档是普通的旧 JavaScript 对象 (POJO),而不是 Mongoose 文档。。。。本质上,它不是返回 Mongoose 文档,而是返回 JSON 格式的数据。至少就我而言,这只是格式问题。 (2认同)

小智 41

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

npm i -D handlebars@4.5.0


Dro*_*rah 11

今天,我收到了来自车把的相同警告,并且视图是空的。以下是我解决这个问题的方法:

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

users.hbs 文件

<ul>
{{#each usersDocuments}}
<li>name: {{this.name}} location: {{this.location}}</li>
{{/each}}    
</ul>
Run Code Online (Sandbox Code Playgroud)

创建一个以context自己的属性命名的全新对象,然后将其传入渲染函数将解决该问题...

笔记:

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


小智 9

“哇,这有效,为什么会发生这种情况?我目前正在使用 express-handlebars (3.1.0),我在我的 express 应用程序中将其设置为渲染引擎。” – 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 - 使用此代码段作为示例重写您的快速服务器

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

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

你的猫鼬查询

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

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


小智 7

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

如果您使用的是 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')
Run Code Online (Sandbox Code Playgroud)


小智 5

尝试 npm install handlebars 版本 4.5.3

npm 安装把手@4.5.3

它对我有用


Dev*_*eeq 5

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

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