Skip to main content

NoSQL injection

Need

Prevention of NoSQL injection attacks

Context

  • Usage of TypeScript for statically typed JavaScript development
  • Usage of Express for building web applications and handling HTTP requests
  • Usage of body-parser for parsing request bodies in Express
  • Usage of MongoDB for database storage and management

Description

Non compliant code

import express from 'express';
import bodyParser from 'body-parser';
import { MongoClient } from 'mongodb';

const app = express();
const port = 3000;

app.use(bodyParser.json());

app.post('/users', async (req, res) => {
const { username, password } = req.body;

const client = new MongoClient('mongodb://localhost:27017', { useNewUrlParser: true });
await client.connect();

const db = client.db('mydb');
const collection = db.collection('users');

const query = {
username: { $eq: username },
password: { $eq: password }
};

const user = await collection.findOne(query);

if (user) {
res.json(user);
} else {
res.status(404).json({ error: 'User not found' });
}

client.close();
});

app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});

The vulnerability in the provided code is NoSQL injection. NoSQL injection occurs when untrusted user input is directly used to construct NoSQL queries without proper validation or sanitization.

In this code, the username and password values from the request body are directly used to construct a query object without any validation or sanitization. The query object is then passed to the findOne method of the MongoDB collection.

An attacker can exploit this vulnerability by manipulating the input values to inject malicious statements into the query. For example, an attacker can provide a username value of { $ne: null } to bypass the authentication and retrieve all user records from the database.

To fix this vulnerability, it is recommended to validate and sanitize the user input before using it in the query. This can be done by implementing proper input validation and using parameterized queries or prepared statements to ensure that user input is treated as data and not executable code.

Steps

  • Validate and sanitize the input data before using it in the query
  • Use parameterized queries or prepared statements to prevent NoSQL injection
  • Implement proper error handling and logging to detect and respond to any potential attacks
  • Consider implementing an authentication mechanism to ensure only authorized users can access the endpoint
  • Regularly update and patch the MongoDB driver and server to benefit from the latest security enhancements

Compliant code

import express from 'express';
import bodyParser from 'body-parser';
import { MongoClient } from 'mongodb';

const app = express();
const port = 3000;

app.use(bodyParser.json());

app.post('/users', async (req, res) => {
try {
const { username, password } = req.body;

// Validate and sanitize input data
if (!username || !password) {
throw new Error('Invalid input data');
}

const client = new MongoClient('mongodb://localhost:27017', { useNewUrlParser: true });
await client.connect();

const db = client.db('mydb');
const collection = db.collection('users');

const query = {
username: { $eq: username },
password: { $eq: password }
};

const user = await collection.findOne(query);

if (user) {
res.json(user);
} else {
res.status(404).json({ error: 'User not found' });
}

client.close();
} catch (error) {
console.error(error);
res.status(500).json({ error: 'Internal server error' });
}
});

app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});

The fixed code addresses the vulnerability by implementing input validation and sanitization before executing the NoSQL query.

  1. The code uses the body-parser middleware to parse the request body as JSON.

  2. In the /users route handler, the code first extracts the username and password from the request body.

  3. It then performs input validation by checking if both username and password are present. If any of them is missing, an error is thrown.

  4. The code creates a new instance of MongoClient and connects to the MongoDB server.

  5. It specifies the database (mydb) and collection (users) to perform the query on.

  6. The code constructs a query object using the username and password values. The query object uses the $eq operator to match exact values.

  7. The code uses the findOne method to execute the query and retrieve the user document that matches the query criteria.

  8. If a user is found, the code responds with the user document as JSON. Otherwise, it responds with a 404 error indicating that the user was not found.

  9. Finally, the code closes the MongoDB connection and handles any errors that may occur during the process.

By validating and sanitizing the input data before constructing the NoSQL query, the code mitigates the risk of NoSQL injection vulnerability.

References