ハンドルバー:親の「独自のプロパティ」ではないため、プロパティ「from」を解決するためのアクセスが拒否されました


15

ハンドルバーを使用したサーバー側レンダリングでNodejsバックエンドを使用しています。docハンドルバーからオブジェクトの配列を読み込んだ後、キー「コンテンツ」と「from」を含みます ただし、を使用#eachしてオブジェクトの配列をループしようとすると、「ハンドルバー:親の「独自のプロパティ」ではないため、プロパティ「from」を解決するためにアクセスが拒否されました」というエラーが表示されます。

私はdoc配列でフェッチしたデータを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;
  });

これは私がループしようとしている.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}}

回答:


25

ハンドルバーの開発依存関係をインストールすることでこの問題を解決します

npm i -D handlebars@4.5.0


これはうまくいきましたが、なぜこれが起こっているのですか?現在、Expressアプリのレンダリングエンジンとして設定したExpress-handlebars(3.1.0)を使用しています。
Lee Boon Kong

これは、いくつかの制限により、新しいバージョンのハンドルバーで起こっていたと思いますが、これらの制限に対処する方法がわかりません。
Lee Boon Kong

さて、問題はハンドルバーをサポートするExpressプラグイン間で発生しますが、ハンドルバー4.5.0を保存してフロントエンドのメインエンジンとして使用できるようになったら、コメントでお知らせください。
メイソン

これは機能していません。npm i -D handlebars@4.5.0
Deepak Thakur


13

mongooseを使用する場合、この問題は.lean()を使用してjsonオブジェクトを取得することで解決できます(mongooseオブジェクトの代わりに):

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

3
神のお恵みがありますように!ライフセーバー!
Nick Thenick

1
問題ありませんでした。
Billeh

2
この回答を2回以上賛成できればと思います。
アブドゥス

7

今日、ハンドルバーから同じ警告が出て、ビューが空になっています。以下は私がそれを修正した方法です:

//  * 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独自のプロパティで名前が付けられた新しいオブジェクト全体を作成し、それをレンダー関数に渡すと、問題が解決します...

注意:

新しいオブジェクトを作成しない場合、機密情報、またはプロジェクトのセキュリティを危険にさらす可能性のある情報を誤って公開しやすく、データベースから返されたデータをマッピングし、必要なものだけをビューに渡すことは、良い方法です。 ...


回答ありがとうございます!データの不要な公開を防ぐために、新しいオブジェクトを作成する方が良いようです。
Lee Boon Kong

この仕事をありがとう。
GNETO DOMINIQUE

準備済みのリストから新しいリストを準備することで、レンダリングに2倍の時間を費やしていませんか?
mustafiz012

6

「これでうまくいきましたが、なぜこれが起こっているのですか?現在、Expressアプリのレンダリングエンジンとして設定したExpress-handlebars(3.1.0)を使用しています。」– Lee Boon Kong Jan 12 at 14:13

「以前は、ハンドルバーを使用して、テンプレートから入力オブジェクトのプロトタイプメソッドとプロパティにアクセスできました。この動作から複数のセキュリティ問題が発生しました... handlebars@^4.6.0では、オブジェクトプロトタイプへのアクセスカスタムクラスをハンドルバーへの入力として使用すると、コードは機能しなくなります...このパッケージは、各テンプレート呼び出しにランタイムオプションを自動的に追加し、セキュリティ制限を無効にします ... ユーザーがテンプレートをサーバーで実行する場合は、このパッケージを使用するのではなく、問題を解決する他の方法を見つけてください...テンプレートインスタンスに渡す前に、クラスインスタンスをプレーンJavaScriptオブジェクトに変換することをお勧めします。アクセスするすべてのプロパティまたは関数は、その親の「独自のプロパティ」である必要があります。」– README

詳細はこちら:https : //www.npmjs.com/package/@handlebars/allow-prototype-access

迅速で汚れた安全でない方法

使用法(express-handlebarsおよびmongoose):

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

3-サーバーを実行して、楽しいダンスをします。


より安全な方法

AJAX呼び出しによって返されたオブジェクトをHandlebarsテンプレートに渡す前に、.hbsファイル内でアクセスする必要がある各プロパティまたは関数を使用して、オブジェクトを新しいオブジェクトにマップします。以下に、ハンドルバーテンプレートに渡す前に作成された新しいオブジェクトを示します。

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

5

npm install handlebars version 4.5.3を試してください

npm install handlebars@4.5.3

それは私のために働いた


これはコメントである必要があります
Arun Vinoth

私は現在Express-handlebars、バージョン3.1.0を使用していました
Lee Boon Kong

ありがとう、私はあなたと@Masonのanwserの両方を試しましたが、なぜこれが起こっているのかわかりません
Lee Boon Kong

3

バージョン4.6.0以降、ハンドルバーはデフォルトでコンテキストオブジェクトのプロトタイププロパティとメソッドへのアクセスを禁止しています。これは、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')

これでうまくいきました。それで、エクスプレスハンドルバーを使用する必要があるときは、毎回これを行う必要がありますか?
Yash Boura

2

このエラーの原因となったハンドルバーの最近のリリースに重大な変更がありました。

ドキュメントで提案されている構成を単に追加することもできますが、実装によっては、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;
  });

ああ、そこにオプションを追加します。どうもありがとうございます。
Lee Boon Kong

1
これは私にはうまくいきませんでした。オプションオブジェクトではなく、コールバックが必要です。
mrg95

0

によって返されたデータから別の新しいオブジェクトまたは配列を作成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")
});
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.