Aller au contenu

Index

Injection NoSQL

Description

L'injection NoSQL est une vulnérabilité de sécurité qui se produit lorsque des attaquants exploitent les faiblesses des applications utilisant des bases de données NoSQL. Elle consiste à injecter des données malveillantes ou inattendues dans les champs de saisie, permettant aux attaquants de manipuler les requêtes et d'accéder à des informations non autorisées.

De manière similaire aux attaques par injection SQL contre les bases de données SQL, l'injection NoSQL cible les applications utilisant des bases de données NoSQL telles que MongoDB, Cassandra ou autres. Les attaquants créent des entrées, telles que des caractères spéciaux ou des charges utiles, pour manipuler les requêtes de base de données NoSQL exécutées par l'application.

En injectant l'entrée conçue, les attaquants cherchent à modifier la logique des requêtes de base de données, à contourner l'authentification ou à récupérer des informations sensibles stockées dans la base de données. Les attaques par injection NoSQL peuvent conduire à un accès non autorisé aux données, à leur modification ou même à la compromission complète de la base de données, affectant ainsi la confidentialité, l'intégrité et la disponibilité de l'application et de ses données.

Exemples

  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);
  }
  ?>

Recommandation

Pour prévenir les attaques par injection NoSQL, considérez les mesures suivantes :

  • Construction sécurisée des requêtes : Construisez les requêtes en utilisant les méthodes spécifiques de la base de données ou les constructeurs de requêtes fournis par l'API. Évitez de concaténer directement les entrées des utilisateurs dans les requêtes. Utilisez plutôt les mécanismes appropriés fournis par l'API de la base de données pour une construction sécurisée des requêtes.

  • Validation et assainissement des entrées : Implémentez une validation robuste des entrées pour vous assurer que les données fournies par l'utilisateur respectent les formats attendus. Utilisez des techniques d'assainissement telles que l'autorisation de caractères acceptables sur une liste blanche pour empêcher toute entrée indésirable.

  • Utiliser une liste blanche de clés acceptées : Pour empêcher l'injection d'opérateurs, utilisez une liste blanche de clés acceptées pour empêcher l'injection d'opérateurs de requête tels que $where, $in, $ne.

Exemples

  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();
  }
  ?>

Liens

Normes

  • GDPR:
    • ART_5
    • ART_32
  • PCI_STANDARDS:
    • REQ_6_2
    • REQ_6_3
    • REQ_6_5
    • REQ_8_6
  • SOC2_CONTROLS:
    • CC_2_1
    • CC_3_4
    • CC_4_1
    • CC_7_1
    • CC_7_2
    • CC_7_4
    • CC_7_5
  • HIPAA_CONTROLS:
    • SECURITY212
    • SECURITY213
    • SECURITY255