【问题标题】:Express.js request body __proto__Express.js 请求正文 __proto__
【发布时间】:2013-11-15 14:08:32
【问题描述】:

我对 express.urlencoded() 中间件生成的 request.body 有一点问题。在某些情况下,它会在 request.body 对象的末尾添加__proto__,而不是像var user = new User(req.body) 那样直接用于启动moongose 模型

作为示例,我将使用 node-express-mongoose-demo 存储库。所有表单都可以正常工作,但 app.post('/users', users.create) 收到 req.body 被 __proto__ “污染”

提前感谢您的帮助

【问题讨论】:

    标签: javascript node.js mongodb express mongoose


    【解决方案1】:

    我知道这个问题被问到现在已经 5 年了,但实际上我昨天遇到了同样的问题。

    我有好消息要分享 - 看起来 mongoose 5.3.9 确实解决了这个问题。您可以使用包含__proto__ 的对象创建新模型。不过,不确定这是否会在未来持续下去。

    另外,qs 模块也可以升级到最新版本来解决这个问题。

    用于测试的代码:

    // simulate object creation by express
    let newCustomer = Object.create(null);
    newCustomer.name = 'new test customer';
    newCustomer.__proto__ = Object.prototype;
    
    console.log(newCustomer); // { name: 'new test customer', __proto__: {} }
    Customer.create(newCustomer, function(err, created) {
        console.log('err:', err, 'created:', created);
        // mongose 5.3.8: ValidationError: Customer validation failed
        // mongose 5.3.9: new customer created
    })
    

    更多细节:

    proto 存在问题,因为 qs 模块使用以下方法创建新对象:

    Object.create(null)
    

    然后当restoreProto被调用时,它会尝试修复对象的原型:

    obj.__proto__ = Object.prototype;
    

    __proto__ 最终成为对象的可见属性:

    let obj = Object.create(null);
    obj.__proto__ = Object.prototype;
    console.log(Object.keys(obj));
    // [ '__proto__' ]
    

    如果新对象是用{} 甚至Object.create(Object) 创建的,那么__proto__ 不会出现在键枚举中,即使以相同的方式分配:

    let obj = {};
    obj.__proto__ = Object.prototype;
    console.log(Object.keys(obj));
    // []    
    

    有趣的事实 - 这种行为随着时间的推移而改变。在节点 v0.10.28 中,两个代码 sn-ps(嗯,用 var 而不是 let ;))都会产生空数组。

    另一个有趣的事情是,较新版本的 qs 模块以不同的方式创建对象,因此它不再导致此问题。

    【讨论】:

      【解决方案2】:

      看来问题出在urlencoded 中间件,它包含在Express 3 中。

      一个可能的解决方案是不使用 Express bodyParser,而是使用 body-parser 模块。

      代替

      app.use(express.urlencoded())
      

      你可以写

      var bodyparser = require('body-parser')
      
      ..........
      
      app.use(bodyparser.urlencoded())
      

      问题似乎来自qs 模块(express 3 模块使用的版本)。它强制在它构建的对象上添加__proto__。上个版本没有这个问题。

      【讨论】:

        【解决方案3】:

        嗯,这很有趣。 __proto__ 是某些 javascript 实现(包括 node/v8)中所有对象的特殊/自动/内部属性。不过,我还没有看到猫鼬做这种事情。将传递给模型构造函数的属性转换为模型/文档实例is here 的代码。不过我没有看到任何可疑之处。

        您是否确切知道会发生这种情况,您确定是urlencoded 做的吗?当您尝试保存已被此“污染”的用户时会发生什么?通常 mongoose 会忽略未在模式中定义的字段,那么会发生什么?

        您应该能够(也许?)使用如下中间件解决此问题,但我很想真正隔离并了解根本原因。 underscore/lodash 有 omit 在这里效果很好。

        var _ = require('lodash');
        
        function unpollute(req, res, next) {
          req.body = _.omit(req.body, '__proto__');
          next();
        }
        app.use(express.urlencoded());
        app.use(unpollute);
        

        然后当你的路由处理程序运行时,req.body 不会有__proto__

        【讨论】:

        • 这种解决方法可以像这样重新创建 body 对象:var fields = Object.create(req.body) 但您真的知道为什么有时会添加 proto 而有时不会添加吗?
        • 我没有看到对象的创建方式有什么奇怪的地方,所以我不确定req.body.proto 来自哪里,但也许您的应用程序中的其他一些中间件会污染它? github.com/senchalabs/connect/blob/…
        • 我发现当表单具有enctype="multipart/form-data" 属性时,__proto__ 没有被添加。所以多部分中间件不会产生额外的密钥。
        • 当您尝试使用“污染”的 req.body 启动 moongose 模型时,您最终会在 mongoose/lib/document.js line 449 中出现错误 500 TypeError: Cannot read property 'ref' of undefined
        猜你喜欢
        • 2016-09-17
        • 1970-01-01
        • 1970-01-01
        • 2013-12-21
        • 1970-01-01
        • 1970-01-01
        • 2018-11-27
        • 2011-11-05
        • 1970-01-01
        相关资源
        最近更新 更多