FlawOpen

flawopen.com/Referencia/Inyección NoSQL en MongoDB: cuando la entrada cambia la consulta

Inyección NoSQL en MongoDB: cuando la entrada cambia la consulta

Guía de seguridad de bases de datos

MongoDB no ejecuta SQL, así que concatenar cadenas SQL no es el problema. Puede existir NoSQL Injection si la aplicación acepta un objeto anidado no confiable donde esperaba un valor escalar y lo pasa a un filtro MongoDB. Es un fallo de validación de entrada de la aplicación, no una CVE del servidor MongoDB.

En palabras sencillas: un valor se convierte en una regla

Imagina que un formulario pide un nombre, pero quien lo atiende acepta un conjunto completo de instrucciones en ese campo. La base de datos sigue las instrucciones en vez de comparar un nombre. Valida el tipo de valor esperado y construye tú la regla de búsqueda.

Límite de confianza: del JSON de la solicitud al filtro MongoDB

Filtro de consulta
Documento BSON que MongoDB interpreta como comparaciones de campos y, si están presentes, operadores de consulta.
Inyección de operadores
La aplicación recibe una estructura controlada por el usuario donde esperaba un único valor literal.
Validación de escalares
Comprobar tipo, longitud y formato permitido antes de que la entrada influya en una operación de base de datos.
Proyección
Lista de campos elegida por el servidor para limitar la exposición accidental de datos.

Cómo ocurre el fallo

1

El endpoint espera un escalar

El contrato de la API define username como una cadena, pero el analizador JSON también admite matrices, objetos, booleanos y null.

2

La aplicación omite la validación del tipo

El controlador inserta el valor analizado directamente en un filtro creado por el servidor; el objeto conserva su estructura de consulta en vez de ser una cadena literal.

3

MongoDB evalúa la estructura recibida

Si el documento anidado contiene operadores de consulta, el predicado puede diferir de la comparación exacta prevista. Los registros afectados dependen de la consulta y de las comprobaciones de autorización de la aplicación.

4

La respuesta determina la exposición

Los campos y la cantidad de registros devueltos dependen de la proyección, el endpoint y los privilegios de la cuenta de base de datos. Esto no demuestra por sí solo una omisión de autenticación ni acceso a toda la base de datos.

Ámbito y evidencias

La documentación oficial de los drivers de MongoDB describe este límite cuando un endpoint convierte JSON sin validar en BSON y lo usa como filtro. Esta guía modela ese fallo de aplicación. No afirma que todas las instalaciones de MongoDB sean vulnerables, no identifica una CVE del servidor ni ofrece una receta para eludir la autenticación.

Patrones vulnerables y reforzados en Node.js

VULNERABLE — UNVALIDATED VALUE ENTERS FILTER
app.post("/users/lookup", async (req, res, next) => {
  try {
    // The route expects a string, but req.body.username is untrusted JSON.
    const user = await users.findOne({ username: req.body.username });
    res.json(user ? { username: user.username } : null);
  } catch (error) {
    next(error);
  }
});
HARDENED — VALIDATE SHAPE, BUILD FILTER, VERIFY HASH
const loginSchema = z.object({
  username: z.string().trim().min(1).max(128),
  password: z.string().min(1).max(1024)
}).strict();

app.post("/login", async (req, res, next) => {
  try {
    const parsed = loginSchema.safeParse(req.body);
    if (!parsed.success) return res.sendStatus(400);

    const { username, password } = parsed.data;
    // The application owns the filter key; input supplies only a string value.
    const user = await users.findOne(
      { username },
      { projection: { _id: 1, username: 1, passwordHash: 1 } }
    );
    const passwordMatches = user &&
      await argon2.verify(user.passwordHash, password);
    if (!passwordMatches) return res.sendStatus(401);

    return res.sendStatus(204); // Session creation is application-specific.
  } catch (error) {
    next(error);
  }
});

El flujo reforzado rechaza los campos que no sean cadenas antes de acceder a la base de datos, no acepta filtros enviados por el cliente, construye en el código de la aplicación una clave fija para el filtro, devuelve solo los campos seleccionados y verifica el hash de la contraseña después de buscar al usuario. El ejemplo supone que z es Zod, users es la colección de MongoDB y argon2 es una biblioteca configurada para el hash de contraseñas; la creación de la sesión queda fuera del ejemplo.

Comprobaciones para verificar la corrección

Fuentes primarias

Explora guías de seguridad relacionadas:Abrir el directorio de seguridad →
Véase también Inyección SQL (CWE-89): cómo funciona y cómo evitarla en cada lenguaje — flawopen.com SQL Injection en IDs numéricos: ¿riesgo? ¿Qué es una consulta parametrizada? ¿Prisma $queryRaw es seguro frente a SQL Injection? ¿Es segura la interpolación en where() de Rails? ¿Sequelize query() es seguro frente a SQL Injection? ¿SQLAlchemy text() es seguro frente a SQL Injection?