Michael Samuel
Michael Samuel

Reputation: 3920

Prevent _id field from being supplied in a MongoDB query

I'm using Mongoose in NodeJS to control a MongoDB database.

I'm creating an API and for obvious security reasons, I want to prevent the auto generated document _id field from getting replaced by a manually generated one in the API request.

Schema:

{ name: String }

Creating a document:

const record = {
    _id: '5e35517cc894c90327a34baf'
    name: 'bob'
}

const insertRecords = async () => {
    await Quiz.create(record);
};

insertRecords();

Results in the following document:

{
   _id: '5e35517cc894c90327a34baf'
   name: 'bob'
}

As can be seen, the _id supplied in the query, as long as it's a valid ObjectID, would replace the _id that was supposed to be auto generated by mongo.

Is there a way to check if this _id field is in the query so that I can reject the API request? The .create method triggers the pre save middleware hook which would always have the _id of the final document so I cannot depend on it to know whether the _id was in the query or it's the auto generated one.

The only option I found is to disable the _id field altogether but this does not make sense.

Upvotes: 0

Views: 517

Answers (1)

Nicholas
Nicholas

Reputation: 2863

Solution #1 - Use .create() method with an explicit object.

It's actually easier than you think. This is self-explanatory - we only define what we want to allow. Mongoose will ignore anything that's not in the object.

const record = {
    _id: '5e35517cc894c90327a34baf'
    name: 'bob'
}

const insertRecords = async () => {
    await Quiz.create({
      name: record.name // only allow names.
    });
};

insertRecords();

Solution #2 - Define a function to clear unwanted objects.

You can define a helper function to clear out unwanted fields.

const filterObj = (obj, ...allowedFields) => {
  const newObject = {};

  // If the current field is one of the allowed fields, keep them in the new object.
  Object.keys(obj).forEach((el) => {
    if (allowedFields.includes(el)) {
      newObject[el] = obj[el];
    }
  });

  return newObject;
};

How to use:

const filteredRecord = filterObj(record, 'name'); // arbitrary list of allowed fields. In this case, we'll only allow 'name'.
await Quiz.create(filteredRecord);

Upvotes: 1

Related Questions