Index
Inyección NoSQL
Descripción
La inyección NoSQL es una vulnerabilidad de seguridad que ocurre cuando los atacantes explotan debilidades en aplicaciones que utilizan bases de datos NoSQL. Implica inyectar datos maliciosos o inesperados en los campos de entrada, lo que permite a los atacantes manipular consultas y acceder a información no autorizada.
De manera similar a los ataques de inyección SQL contra bases de datos SQL, la inyección NoSQL se dirige a aplicaciones que utilizan bases de datos NoSQL como MongoDB, Cassandra u otras. Los atacantes crean entradas, como caracteres especiales o cargas útiles, para manipular las consultas de bases de datos NoSQL ejecutadas por la aplicación.
Al inyectar la entrada manipulada, los atacantes buscan alterar la lógica de las consultas de la base de datos, eludir la autenticación o recuperar información confidencial almacenada en la base de datos. Los ataques de inyección NoSQL pueden conducir al acceso no autorizado a datos, modificación de datos o incluso al compromiso completo de la base de datos, afectando la confidencialidad, integridad y disponibilidad de la aplicación y sus datos.
Ejemplos
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);
}
?>
Recomendación
Para prevenir los ataques de inyección NoSQL, considere las siguientes medidas:
-
Construcción segura de consultas: Construya consultas utilizando los métodos específicos de la base de datos o los constructores de consultas proporcionados por la API. Evite concatenar directamente las entradas de los usuarios en las consultas. En su lugar, utilice los mecanismos adecuados proporcionados por la API de la base de datos para una construcción segura de consultas.
-
Validación y saneamiento de entradas: Implemente una validación de entrada robusta para garantizar que los datos proporcionados por el usuario cumplan con los formatos esperados. Utilice técnicas de saneamiento como listas blancas de caracteres aceptables para evitar entradas no deseadas.
-
Usar lista blanca de claves aceptadas: Para evitar la inyección de operadores, use una lista blanca de claves aceptadas para evitar la inyección de operadores de consulta como
$where,$in,$ne.
Ejemplos
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();
}
?>
Enlaces
Estándares
- 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