Querying. Generally this is not a problem as for { x : user_obj }, dollar signs are not top level and have no effect. In theory one might let the user build a query completely themself and provide it to the database. In that case checking for $ characters in keynames is important. That however would be a highly unusual case.
One way to handle user-generated keys is to always put them in sub-objects. Then they are never at top level (where $operators live) anyway.
// A malicious user's login request:{"username": "admin","password": { "$exists": true }}// Somewhere deep inside the server…var user = db.users.find({ username: request.username, password: request.password });if (user) {// …}
--
You received this message because you are subscribed to the Google Groups "mongodb-dev" group.
To view this discussion on the web visit https://groups.google.com/d/msg/mongodb-dev/-/LZjKXrZBQxoJ.
To post to this group, send email to mongo...@googlegroups.com.
To unsubscribe from this group, send email to mongodb-dev...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/mongodb-dev?hl=en.
I hope that makes the distinction clear between input data and queries
(the bson/object structure). If you need an example of what you have
to do to go out of your way to do to enable this kind of hole I'd be
happy to provide a simple example.
If you need an example of what you have to do to go out of your way to do to enable this kind of hole I'd be happy to provide a simple example.
var express = require('express'),mongodb = require('mongodb'),app = express.createServer(express.bodyParser()),db = new mongodb.Db('someDatabase', new mongodb.Server('127.0.0.1', 27017, {})),users;app.post('/login', function(req, res){res.header('Content-Type', 'text/plain; charset=utf-8');users.findOne({ username: req.body.username, password: req.body.password }, function(err, user){if (err) {res.send("Error");} else if (user) {res.send('logged in as ' + user.username + '\n');} else {res.send('failed to log in\n');}});});db.open(function(err){if (err) throw err;db.collection('users', function(err, collection){if (err) throw err;users = collection;app.listen(3000);});});
curl -H "Content-Type: application/json" -d '{ "username": "foo", "password": "bar" }' http://localhost:3000/logincurl -H "Content-Type: application/json" -d '{ "username": "foo", "password": "what" }' http://localhost:3000/logincurl -H "Content-Type: application/json" -d '{ "username": "foo", "password": { "$exists": true } }' http://localhost:3000/login
I'm sorry but this sort of basic web security should be part of any
web application. Passing untreated user input directly to your
database is foolish at best and most people would consider it
unprofessional. Blaming the database for doing "bad things" when you
pass it bad data is... disingenuous...
--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
World Singles, LLC. -- http://worldsingles.com/
"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)
> I'm sorry but this sort of basic web security should be part of any
> web application. Passing untreated user input directly to your
> database is foolish at best and most people would consider it
> unprofessional. Blaming the database for doing "bad things" when you
> pass it bad data is... disingenuous...
Why should developers need to sanitize data that they hand off to a driver?
I don’t believe that it is immediately obvious, either, how to handle user input safely, especially when it can be legitimately be stuff other than strings (for example, any API that takes its requests as JSON).
You can’t look at a line of code like
db.posts.find({ owner: user, tags: { $all: tags } });
and tell if its inputs are safe. Is `user` or `tags` untrusted? Have they both been cast to strings? If `tags` isn’t supposed to be a string, has it been filtered for operators? Can operators even be used inside $all (if not, `tags` is OK no matter what, right?) If the rules aren’t simple, it’s tricky to audit your code.
I think it would make more sense if you could tell by looking at that one line of code whether the input’s being handled in a safe way:
// May or may not be safe:
db.sessions.find({ _id: session_id, username: username });
As policy, you could decide that any value which gets used in a query must be cast to a string in the same place it’s incorporated into the query object:
// Definitely safe, if everything can a string:
db.sessions.find({ _id: session_id.toString(), username: username.toString() });
But, that doesn’t work if not all of the values are strings (`session_id` might be an Object ID, for instance). Personally, I think that it would be much nicer to let you show the driver what should be treated as data:
// Definitely safe, needs driver support
db.sessions.find({ _id: { $: session_id }, username: { $: username } });
The driver would decide how to pass it safely to the database. The responsibility would be out of developers’ hands beyond “use your driver like this”.
(There might be a better solution — but “don't pass through [untrusted] json” being the state of things makes me really uncomfortable. I’m going to open an issue on node-mongodb-native (just because that’s the driver I use) and, if I have a chance, start building support for something like this.)
--
You received this message because you are subscribed to the Google Groups "mongodb-dev" group.
To post to this group, send email to mongo...@googlegroups.com.
To unsubscribe from this group, send email to mongodb-dev...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/mongodb-dev?hl=en.
> Mongoose (mongoosejs.com) for nodejs uses schemas for all collections. Each query is casted to its defined type before passing it down to the driver (node-mongodb-native).
Interesting… how do you tell it the difference between an intentional part of the query (e.g. `{ someValue: { $gt: value } }`) and an unintentional one (e.g. `{ someValue: valueFromUser`)?
Maybe the basic misunderstanding.her is that the drivers do not send queries as strings to the server but instead send bson. The server does not evaluate strings as structured elements, with a few exceptions.
Absolutely, and that’s something that I love about MongoDB.
Is it reasonable for me to argue that untrusted inputs aren’t necessarily strings either?
Interesting… how do you tell it the difference between an intentional part of the query (e.g. `{ someValue: { $gt: value } }`) and an unintentional one (e.g. `{ someValue: valueFromUser`)?