Skip to content

NoSQL Injection

NoSQL Injection

Description

NoSQL injection is a security vulnerability that occurs when attackers exploit weaknesses in applications using NoSQL databases. It involves injecting malicious or unexpected data into input fields, enabling attackers to manipulate queries and access unauthorized information.

Similar to SQL injection attacks against SQL databases, NoSQL injection targets applications utilizing NoSQL databases like MongoDB, Cassandra, or others. Attackers craft input, such as special characters or payloads, to manipulate NoSQL database queries executed by the application.

By injecting the crafted input, attackers aim to alter the logic of database queries, bypass authentication, or retrieve sensitive information stored in the database. NoSQL injection attacks can lead to unauthorized data access, data modification, or even the complete compromise of the database, compromising the confidentiality, integrity, and availability of the application and its data.

Examples

  import com.mongodb.*;
  import org.springframework.web.bind.annotation.*;

  @RestController
  @RequestMapping("/login")
  public class NoSQLInjectionController {

      @PostMapping
      public void login(@RequestParam String userName, @RequestParam String password) {
          MongoClientURI uri = new MongoClientURI("mongodb://localhost:27017");
          MongoClient mongoClient = new MongoClient(uri);

          try {
              DB database = mongoClient.getDB("test");
              DBCollection collection = database.getCollection("users");

              BasicDBObject query = new BasicDBObject();
              query.put("$where", "this.sharedWith == \"" + userName + "\" && this.password == \"" + password + "\"");

              DBCursor cursor = collection.find(query);

              while (cursor.hasNext()) {
                  System.out.println(cursor.next());
              }
          } finally {
              mongoClient.close();
          }
      }
  }
  const MongoClient = require('mongodb').MongoClient;
  const express = require('express');
  const app = express();

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

      const uri = 'mongodb://localhost:27017';
      const client = new MongoClient(uri);

      try {
          await client.connect();
          const database = client.db('test');
          const usersCollection = database.collection('users');

          // Vulnerable query susceptible to NoSQL Injection
          query = { $where: `this.username == '${username}' && this.password == '${password}'` }
          const user = await usersCollection.find(query);

          res.json({ success: user !== null });
      } finally {
          await client.close();
      }
  });

  app.listen(3000, () => {
      console.log('Server started on port 3000');
  });
  <?php
  $username = $_POST['username'];
  $password = $_POST['password'];

  $manager = new MongoDB\Driver\Manager('mongodb://localhost:27017');

  $query = [ "username" => $username, 'password' => $password ];
  $testquery = new MongoDB\Driver\Query($query, []);

  $cursor = $manager->executeQuery('test.users', $testquery);

  foreach ($cursor as $document) {
      var_dump($document);
  }
  ?>

Recommendation

To prevent NoSQL injection attacks, consider the following measures:

  • Input Validation and Sanitization: Implement robust input validation to ensure that user-supplied data meets expected formats. Use sanitization techniques like whitelisting acceptable characters to prevent unwanted input.

  • Safe Query Construction: Construct queries using the database's specific methods or query builders provided by the API. Avoid directly concatenating user inputs into queries. Instead, use the appropriate mechanisms provided by the database API for safe query construction.

  • Least Privilege Principle: Limit the permissions granted to the application's database user. Ensure the user has only necessary privileges to reduce potential attack surface.

Examples

  import com.mongodb.*;
  import java.util.regex.Pattern;

  public class SecureNoSQLInjection {
      public static void main(String[] args) {
          String username = sanitizeInput(args[0]);
          String password = sanitizeInput(args[1]);

          MongoClientURI uri = new MongoClientURI("mongodb://localhost:27017");
          MongoClient mongoClient = new MongoClient(uri);

          DB database = mongoClient.getDB("test");
          DBCollection collection = database.getCollection("users");

          BasicDBObject query = new BasicDBObject();
          query.put("username", username);
          query.put("password", password);

          DBCursor cursor = collection.find(query);

          while (cursor.hasNext()) {
              System.out.println(cursor.next());
          }
      }

      // Function to sanitize input (escape special characters)
      private static String sanitizeInput(String input) {
          return Pattern.quote(input); // Escapes special characters in the input string
      }
  }
  const MongoClient = require('mongodb').MongoClient;
  const express = require('express');
  const mongoSanitize = require('mongo-sanitize');
  const app = express();

  // Middleware to parse incoming JSON requests
  app.use(express.json());

  app.post('/login', async (req, res) => {
      const username = mongoSanitize.sanitize(req.body.username);
      const password = mongoSanitize.sanitize(req.body.password);

      const uri = 'mongodb://localhost:27017';
      const client = new MongoClient(uri);

      try {
          await client.connect();
          const database = client.db('test');
          const usersCollection = database.collection('users');

          // Secure query using parameterization to prevent NoSQL Injection
          const user = await usersCollection.findOne({ username, password }); // Secure code with input sanitization

          res.json({ success: user !== null });
      } finally {
          await client.close();
      }
  });

  app.listen(3000, () => {
      console.log('Server started on port 3000');
  });
  <?php
  $username = $_POST['username'] ?? null;
  $password = $_POST['password'] ?? null;

  $sanitizedUsername = filter_var($username, FILTER_SANITIZE_STRING);

  $manager = new MongoDB\Driver\Manager('mongodb://localhost:27017');

  // Using prepared statements for the query
  $query = new MongoDB\Driver\Query([
      'username' => $sanitizedUsername,
      'password' => ['$eq' => $password] // Use an exact match for the password
  ]);

  try {
      $cursor = $manager->executeQuery('test.users', $query);

      foreach ($cursor as $document) {
          var_dump($document);
      }
  } catch (MongoDB\Driver\Exception\Exception $e) {
      echo "Exception: ", $e->getMessage();
  }
  ?>

Standards

  • GDPR:
    • ART_5
    • ART_32
  • PCI_STANDARDS:
    • REQ_6_2
    • REQ_6_3
    • REQ_6_5
    • REQ_8_6