跳转至

Index

NoSQL注入

描述

NoSQL注入是一种安全漏洞,当攻击者利用使用NoSQL数据库的应用程序中的弱点时就会发生。它涉及将恶意或意外的数据注入输入字段,使攻击者能够操作查询并访问未经授权的信息。

与针对SQL数据库的SQL注入攻击类似,NoSQL注入以使用MongoDB、Cassandra等NoSQL数据库的应用程序为目标。攻击者构造输入,例如特殊字符或有效负载,以操作由应用程序执行的NoSQL数据库查询。

通过注入精心构造的输入,攻击者旨在改变数据库查询的逻辑、绕过身份验证或检索存储在数据库中的敏感信息。NoSQL注入攻击可能导致未经授权的数据访问、数据修改,甚至完全危及数据库的安全,从而损害应用程序及其数据的机密性、完整性和可用性。

示例

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

建议

为防止NoSQL注入攻击,请考虑以下措施:

  • 安全查询构造:使用数据库的特定方法或API提供的查询生成器来构造查询。避免将用户输入直接连接到查询中。相反,请使用数据库API提供的适当机制来进行安全的查询构造。

  • 输入验证和清理:实施稳健的输入验证,以确保用户提供的数据符合预期的格式。使用诸如将可接受字符列入白名单的清理技术来防止有害的输入。

  • 使用可接受键的白名单:为了防止操作符注入,请使用接受键的白名单以防止查询操作符(如$where$in$ne)的注入。

示例

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

链接

标准

  • 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