Express.js请求体__proto__

3
我在使用express.urlencoded()中间件生成的request.body时遇到了一些小问题。在某些情况下,它会在request.body对象末尾添加__proto__,这样就无法直接用来初始化moongose模型,如下所示:var user = new User(req.body)。
node-express-mongoose-demo存储库为例。所有表单都可以正常工作,但是app.post('/users', users.create)接收到的req.body被“污染”了,带有__proto__。
感谢您提前的任何帮助。
3个回答

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__。但是最新版本没有这个问题。

0
嗯,这很有趣。在一些JavaScript实现中,包括node/v8,__proto__是所有对象上的特殊/自动/内部属性。虽然我没有看到mongoose做这种事情。将传递给模型构造函数的属性转换为模型/文档实例的代码在这里。但我在那里没有看到任何可疑的东西。
你知道确切发生在哪些情况下吗?你确定是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__,有时不会? - Michał Zbytniewski
我在对象创建方面没有看到任何奇怪的地方,所以我不确定 req.body.proto 是从哪里来的,但也许你应用程序中的其他一些中间件正在污染它? https://github.com/senchalabs/connect/blob/7edb875a9f305e38f4d960fa46ac674038241892/lib/middleware/urlencoded.js#L47 - Peter Lyons
我发现当表单有属性 enctype="multipart/form-data" 时,__proto__ 不会被添加。因此,多部分中间件不会生成额外的键。 - Michał Zbytniewski
当你试图使用“被污染”的req.body初始化moongose模型时,最终会出现错误500 TypeError:Cannot read property 'ref' of undefined,位于mongoose/lib/document.js line 449 - Michał Zbytniewski

0

我知道这个问题已经被问了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中,这两个代码片段(使用var而不是let ;))都会产生空数组。
另一个有趣的事情是,较新版本的qs模块以不同的方式创建对象,因此不再引起此问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接