ofensiva
inyección LDAP
LDAP injection
CWE-90

Inyección LDAP: qué es, ataques y prevención

Qué es la inyección LDAP (CWE-90): manipulación de filtros para bypass de autenticación y extracción de datos, y cómo prevenirla con escapado.

Secra6 de julio de 202611 min de lectura

La inyección LDAP (LDAP injection) es una vulnerabilidad web en la que un atacante introduce metacaracteres del lenguaje de filtros LDAP dentro de los datos que envía a una aplicación, de forma que el servidor de directorio interpreta esa entrada como parte de la consulta y no como un simple valor. Cuando el código construye el filtro de búsqueda concatenando directamente la entrada del usuario, esos fragmentos alteran la lógica original: permiten saltarse la autenticación, enumerar usuarios, leer atributos que no deberían ser accesibles o falsear el resultado de una comprobación de permisos. Pertenece a la misma familia de inyección que la inyección SQL, aunque el intérprete objetivo es distinto, y está catalogada como CWE-90.

Lo esencial. La inyección LDAP nace de mezclar código (la sintaxis del filtro) y datos (la entrada del usuario) en la misma cadena. La defensa correcta no es una lista negra de caracteres, sino escapar la entrada según el RFC 4515 para filtros y el RFC 4514 para nombres distinguidos (DN), preferiblemente a través de una API del framework que lo haga por ti. El resto de capas (validación por allowlist, mínimo privilegio en la cuenta de bind, ACL del directorio) refuerzan esa separación, pero no la sustituyen.

Cómo funciona la inyección LDAP: manipulación de filtros

LDAP (Lightweight Directory Access Protocol) es el protocolo que usan los directorios corporativos como Active Directory, OpenLDAP o 389 Directory Server para almacenar usuarios, grupos y recursos. Muchas aplicaciones lo consultan para autenticar credenciales, buscar personas o resolver la pertenencia a un grupo. El problema aparece cuando la aplicación compone el filtro concatenando datos del usuario: el servidor de directorio no distingue lo que escribió el programador de lo que introdujo el atacante, e interpreta toda la cadena como una única expresión.

Sintaxis de los filtros LDAP

Los filtros de búsqueda LDAP siguen el RFC 4515 y usan notación prefija (polaca) entre paréntesis. Una comparación simple es (uid=ana). Los operadores lógicos se anteponen a las condiciones que agrupan:

  • (&(cond1)(cond2)) es la conjunción (AND): se cumplen todas.
  • (|(cond1)(cond2)) es la disyunción (OR): se cumple al menos una.
  • (!(cond)) es la negación (NOT).
  • El asterisco * es un comodín que representa cualquier secuencia de caracteres.

Los caracteres con significado especial dentro de un filtro son *, (, ), \ y el byte nulo. Si esos caracteres llegan sin escapar desde la entrada del usuario, el atacante puede reescribir la estructura del filtro a su antojo.

Bypass de autenticación

Consideremos un login que valida credenciales contra el directorio construyendo el filtro por concatenación:

# VULNERABLE: la entrada se concatena dentro del filtro LDAP
user = request.form["user"]
pwd  = request.form["pass"]
ldap_filter = "(&(uid=" + user + ")(userPassword=" + pwd + "))"
conn.search("ou=people,dc=empresa,dc=com", ldap_filter)

Si el atacante envía como usuario el valor admin)(&) y cualquier contraseña, la cadena resultante es:

(&(uid=admin)(&))(userPassword=x))

Muchas bibliotecas cliente procesan el primer filtro bien formado, (&(uid=admin)(&)), y descartan el resto. El componente (&) es el filtro de verdad absoluta definido en el RFC 4526: siempre evalúa a verdadero. El resultado es que el filtro se reduce a "el uid es admin", ignorando por completo la comprobación de la contraseña. Es el equivalente LDAP del clásico ' OR '1'='1 de SQL.

La variante con comodín es aún más simple: enviar * como usuario devuelve la primera entrada de la rama, y en formularios mal diseñados equivale a autenticarse como un usuario arbitrario.

Tipos de ataque

Extracción de datos e inyección ciega

Aunque la aplicación no muestre resultados directamente, el atacante puede deducir información observando el comportamiento, igual que en la inyección SQL ciega. La técnica se apoya en los comodines y en los operadores booleanos para preguntar por un valor carácter a carácter. Por ejemplo, para adivinar el correo de la cuenta admin se envían filtros como:

(&(uid=admin)(mail=a*))
(&(uid=admin)(mail=b*))
(&(uid=admin)(mail=ad*))

Cada petición devuelve una respuesta observable distinta (un login que funciona o falla, un mensaje que aparece o desaparece, un tiempo de carga diferente) según la condición sea verdadera o falsa. Con suficientes peticiones se reconstruye el atributo completo.

Modificación de la lógica de autorización

Cuando la aplicación decide permisos consultando la pertenencia a un grupo, un filtro vulnerable permite falsear esa decisión. Si el código comprueba (&(uid=USER)(memberOf=cn=admins,ou=groups,dc=empresa,dc=com)), inyectar una condición que cierre el AND y añada un OR de verdad absoluta hace que la comprobación de grupo deje de ser vinculante, lo que equivale a una escalada de privilegios lógica.

Inyección LDAP frente a inyección SQL

Ambas comparten la causa raíz (mezclar código y datos en una cadena) y la categoría OWASP, pero difieren en detalles que condicionan la explotación y la defensa. LDAP usa notación prefija con paréntesis, no una sintaxis infija con palabras clave, y carece de un carácter de comentario equivalente al -- de SQL: el atacante no "comenta" el resto del filtro, sino que se apoya en que la biblioteca cliente ignore lo que sobra tras el primer filtro válido. Además, LDAP tiene dos contextos de inyección con escapados diferentes, el filtro de búsqueda (RFC 4515) y el nombre distinguido o DN (RFC 4514), y confundirlos deja un hueco explotable. Por eso la defensa no puede copiarse tal cual desde la prevención de la inyección de comandos: el principio es el mismo, pero la implementación depende del contexto LDAP.

Detección: ldapsearch, Burp y payloads manuales

No existe un equivalente tan maduro a sqlmap para LDAP, así que la detección se apoya sobre todo en pruebas manuales guiadas por la metodología OWASP (WSTG-INPV-06, LDAP Injection Testing). El flujo habitual combina la interceptación de peticiones con Burp Suite y la validación posterior con el cliente ldapsearch de OpenLDAP:

# Reproducir un filtro sospechoso contra el directorio para confirmar el hallazgo
ldapsearch -x -H ldap://directorio.empresa.com -b "ou=people,dc=empresa,dc=com" "(&(uid=admin)(&))"

Los payloads de sondeo iniciales son sencillos: un *, un paréntesis suelto ), la secuencia )(, o el filtro de verdad absoluta (&). Si al introducir un ( sin cerrar la aplicación devuelve un error de sintaxis del directorio, hay una fuerte señal de que la entrada llega sin escapar al filtro. A partir de ahí, Burp Intruder ayuda a automatizar la extracción ciega carácter a carácter. Conviene probar todos los puntos de entrada (cabeceras, campos JSON, parámetros de búsqueda), porque cualquiera puede acabar en un filtro.

Prevención: escapado por contexto y mínimo privilegio (CWE-90)

Escapado según RFC 4515 y RFC 4514

La defensa que ataca la causa raíz es escapar la entrada antes de insertarla en el filtro, usando la función del lenguaje pensada para ello en lugar de un reemplazo casero. En un filtro, los caracteres especiales se sustituyen por su secuencia de escape (* pasa a \2a, ( a \28, ) a \29, \ a \5c). Prácticamente todos los ecosistemas incluyen una utilidad específica:

  • PHP: ldap_escape($valor, "", LDAP_ESCAPE_FILTER) y LDAP_ESCAPE_DN para el DN.
  • Python: ldap.filter.escape_filter_chars() en python-ldap, o escape_filter_chars() en ldap3.
  • Java: LdapEncoder.filterEncode() de Spring LDAP, o encodeForLDAP() y encodeForDN() de OWASP ESAPI.
  • .NET: escapado por contexto al construir filtros con System.DirectoryServices.Protocols.

La regla es la misma que en la inyección SQL: ningún dato del usuario debe concatenarse en crudo dentro de un filtro. Y hay que escapar según el contexto correcto, porque el conjunto de caracteres especiales del filtro no coincide con el del DN.

Autenticación por bind, no por comparación de contraseña

Un error de diseño frecuente es meter la contraseña dentro del filtro y comparar el atributo userPassword, lo que expone un segundo punto de inyección y es inseguro por otras razones (el atributo suele estar cifrado). El patrón correcto separa las dos operaciones: primero se busca al usuario por su identificador para obtener su DN, escapando la entrada, y después se realiza un bind autenticado con ese DN y la contraseña facilitada. El resultado del bind decide si las credenciales son válidas, sin que la contraseña llegue nunca a un filtro.

Validación por allowlist, ACL y mínimo privilegio

El escapado se complementa con capas adicionales. La validación por allowlist restringe cada campo a su formato esperado (un identificador que solo admita ^[a-zA-Z0-9._-]+$ deja fuera todos los metacaracteres). Las ACL del directorio deben limitar qué atributos puede leer la cuenta de la aplicación, para que una inyección no derive en una fuga masiva de datos. Y la cuenta de servicio que hace bind debe tener el mínimo privilegio: solo lectura sobre la rama estrictamente necesaria, sin escritura ni acceso a atributos sensibles. Este razonamiento por capas es el mismo que aplicamos en el pentesting de Active Directory.

Casos reales y encaje con OWASP

La inyección LDAP no es un problema teórico. La CVE-2017-14596 afectó a Joomla en versiones 1.5 a 3.7.5: el plugin de autenticación LDAP construía el filtro sin sanear la entrada, lo que permitía una inyección ciega para extraer credenciales del directorio carácter a carácter. El patrón se repite en paneles de administración, portales de acceso y guías de empleados que delegan la autenticación en un directorio corporativo.

En el catálogo público, la inyección LDAP corresponde a CWE-90 (Improper Neutralization of Special Elements used in an LDAP Query) y se clasifica dentro de A03:2021 Injection del OWASP Top 10, la misma categoría que la inyección SQL y la de comandos. Puedes ver el contexto completo en nuestro análisis del OWASP Top 10 2025 y en el repaso de las 5 vulnerabilidades web más comunes.

Preguntas frecuentes

¿En qué se diferencia la inyección LDAP de la inyección SQL?

Ambas nacen de mezclar código y datos y pertenecen a la categoría Injection de OWASP, pero el intérprete objetivo es distinto: la inyección SQL abusa del motor de base de datos y la LDAP del servidor de directorio. LDAP usa notación prefija con paréntesis, carece de carácter de comentario y tiene dos contextos con escapados diferentes (filtro RFC 4515 y DN RFC 4514).

¿Escapar los caracteres especiales es suficiente para prevenirla?

El escapado por contexto es la defensa principal y ataca la causa raíz, siempre que se use la función correcta para cada contexto (filtro frente a DN) y no un reemplazo manual incompleto. Conviene reforzarlo con validación por allowlist, autenticación por bind y ACL de mínimo privilegio en la cuenta de servicio.

¿Qué es la inyección LDAP ciega?

Es la variante en la que la aplicación no muestra los resultados de la consulta, pero su comportamiento cambia según el filtro inyectado. El atacante usa comodines y operadores booleanos para preguntar por un valor carácter a carácter (por ejemplo (&(uid=admin)(mail=a*))) y deduce la información observando qué respuestas indican verdadero o falso.

¿Qué herramientas se usan para detectar inyección LDAP?

En pruebas manuales, Burp Suite para interceptar y modificar los parámetros, y el cliente ldapsearch de OpenLDAP para reproducir y confirmar el filtro contra el directorio. Burp Intruder automatiza la extracción ciega. La metodología de referencia es la OWASP Web Security Testing Guide, en concreto la prueba WSTG-INPV-06.

Recursos relacionados

Auditar inyección LDAP con Secra

Secra realiza auditorías de aplicaciones web alineadas con OWASP Top 10 y la OWASP Web Security Testing Guide. Probamos cada punto de entrada que llega a un directorio (formularios de login, buscadores de personas, comprobaciones de grupo) en busca de inyección LDAP directa y ciega, confirmamos manualmente los hallazgos con ldapsearch y verificamos que el escapado se aplica en el contexto correcto, que la autenticación se resuelve con un bind y no comparando la contraseña en el filtro, y que la cuenta de servicio opera con mínimo privilegio.

Entregamos hallazgos priorizados por impacto, pruebas de concepto reproducibles y recomendaciones de hardening específicas para vuestra arquitectura de directorio. Si necesitáis una revisión profesional, conoced nuestro servicio de auditoría web y móvil o escribidnos desde nuestra página de contacto.

Sobre el autor

Equipo de Secra Solutions

Ethical hackers certificados OSCP, OSEP, OSWE, CRTO, CRTL y CARTE, con más de 7 años de experiencia en ciberseguridad ofensiva. Autores de los CVE-2025-40652 y CVE-2023-3512.

Compartir artículo