eherve / mongoose-datatable

Server side dataTable request support for mongoose
MIT License
42 stars 28 forks source link

Fake requests to get other fields. Options.select not working #26

Closed gigi closed 9 years ago

gigi commented 10 years ago

Hi! Thanx for Your plugin.

I have issue with hiding the unnesesary fields from response.

According to docs I can select fields that I want to send to client:

Model.dataTable(req.query, {
    conditions: conditions,
    select: 'name'
}, callback)

But I can get every field from collection by faking this request:

?sEcho=1&iColumns=2&sColumns=&iDisplayStart=0&iDisplayLength=10&mDataProp_0=name&mDataProp_1=created&sSearch=&bRegex=false&sSearch_0=&bRegex_0=false&bSearchable_0=true&sSearch_1=&bRegex_1=false&bSearchable_1=true&iSortCol_0=0&sSortDir_0=asc&iSortingCols=1&bSortable_0=true&bSortable_1=true&_=1412322544184
{
  "sEcho": "1",
  "iTotalRecords": 1,
  "iTotalDisplayRecords": 1,
  "aaData": [
    {
      "_id": "542bfd18425c0d2d3xxxxx",
      "name": "for test",
      "created": "2014-10-01T13:09:44.632Z" <----- I want to hide this field
    }
  ]
}

Ii it neccessary to parse get request before calling the plugin? Is it a bug or something wrong with my code? Thanx!

eherve commented 9 years ago

Hi,

Thank you for using this plugin. Sorry for my late reply !

In your "fake" request you are requesting the fields name and created. So if you want only name, either remove the mDataProp1=created or in the options.select try to use "-created"

Regards,

gigi commented 9 years ago

Yes, sure) Im trying to say that client can get any field from collection by posting fake request with mDataProp1=ANYFIELDNAME. So EVERYONE can brutforce field names to get sensitive data For example, mDataProp_1=phone, mDataProp_1=user, mDataProp_1=password, mDataProp_1=address, mDataProp_1=email etc.... I think plugin has to deny access to all fields exept fields that in option.select. Security concept "deny all, than allow some" is good for any application I think)

try to use "-created"

I am lazy to hide every field in all collections except needed... And "-created" is not working...

Model.dataTable(req.query, {
    conditions: conditions,
    select: '-created'
}, callback)

Error:

The top-level _id field is the only field currently supported for exclusion (Mongo 2.6.4)

If You want to prevent selecting sensitive fields - better approach is make smth like select: false in model. e.g

  var schema = {
        name: {
            type: String,
            unique: true,
            required: true
        },
        created: {
            type: Date,
            default: Date.now,
            select: false
        }
...

But there are some other bugs in You plugin with the next example

    var schema = {
        name: {
            type: String,
            unique: true,
            required: true,
            select: false
        },
        created: {
            type: Date,
            default: Date.now,
            select: false
        }

I get absolutely all fields in response...

{
  "sEcho": "1",
  "iTotalRecords": 4,
  "iTotalDisplayRecords": 4,
  "aaData": [
    {
      "_id": "54212efec31fdbxxxx",
      "name": "45",
      "users": [],
      "domains": [
        "54240zxxxx"
      ],
      "created": "2014-09-23T08:27:42.350Z",
      "__v": 1,
    }, ....

It is not safe out of the box.

Regards

eherve commented 9 years ago

You are right, in previous documentation I mentionned this:

"If the field is marked as not selectable in the schema (select: false) or if the option dataTableSelect on the field exist and is set to false (dataTableSelect: false) then the field will not be selected even if it was requested by the dataTable client."

But I can't find it anymore, I'll have to update the current documentation.

In the meantime there are these following issues right:

I'll check this as soon as possible and push a fix.

Thanks for your feedbacks

eherve commented 9 years ago

Ok I have updated the documentation and tested the select: false and dataTableSelect: false. It is working for me, I have updated the test. Using options.select = "-FIELD" is not supported by mongodb yet so it is not the good way to do it.

Check in the test folder, maybe I missed something... Let me know

gigi commented 9 years ago

Thank You! I'll try to check tonight!)

gigi commented 9 years ago

So, select: false and dataTableSelect: false works but there is security bug if all requested fields marked with select/dataTableSelect: false:

var mongoose = require('mongoose');
var DataTable = require('mongoose-datatable');
mongoose.plugin(DataTable.init);
var http = require('http');
var url = require('url');
var modelSchema = mongoose.Schema;

mongoose.connect('mongodb://127.0.0.1/test');
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback () {
    console.log('Connected to DB');
});

var schema = {
    name: {
        type: 'String',
        dataTableSelect: false
    },
    password: {
        type: 'String'
    },
    created: {
        type: 'Date',
        default: Date.now(),
        dataTableSelect: false
    }
};
var mongooseSchema = new modelSchema(schema);
var usersModel = mongoose.model('users', mongooseSchema);

var query = '?sEcho=1&iColumns=2&sColumns=&iDisplayStart=0&iDisplayLength=10&mDataProp_0=name&mDataProp_1=created&sSearch=&bRegex=false&sSearch_0=&bRegex_0=false&bSearchable_0=true&sSearch_1=&bRegex_1=false&bSearchable_1=true&iSortCol_0=0&sSortDir_0=asc&iSortingCols=1&bSortable_0=true&bSortable_1=true&_=1413632467737'
var queryData = url.parse(query, true).query;
usersModel.dataTable(queryData, {}, function(err, docs) {
    console.log(err);
    var result = JSON.stringify(docs, null, 4);
    console.log(result);
});

outputs all fields of collection:

{
    "sEcho": "1",
    "iTotalRecords": 2,
    "iTotalDisplayRecords": 2,
    "aaData": [
        {
            "_id": "54424b5bf3e34306fab66920",
            "name": "name1",
            "password": "password1",
            "created": "2014-01-01"
        },
        {
            "_id": "54424b65f3e34306fab66921",
            "name": "name2",
            "password": "password2",
            "created": "2014-01-02"
        }
    ]
}

or real life schema example:

var schema = {
    name: {
        type: 'String'
    },
    password: {
        type: 'String',
        dataTableSelect: false
    },
    created: {
        type: 'Date',
        default: Date.now()
    }
};
var query = '?sEcho=1&iColumns=1&sColumns=&iDisplayStart=0&iDisplayLength=10&mDataProp_0=password&sSearch=&bRegex=false&sSearch_0=&bRegex_0=false&bSearchable_0=true&sSearch_1=&bRegex_1=false&bSearchable_1=true&iSortCol_0=0&sSortDir_0=asc&iSortingCols=1&bSortable_0=true&bSortable_1=true&_=1413632467737'

same output as above