LDAP injection is a web vulnerability in which an attacker inserts metacharacters from the LDAP filter language into the data sent to an application, so that the directory server interprets that input as part of the query rather than as a plain value. When the code builds its search filter by directly concatenating user input, those fragments alter the original logic: they allow bypassing authentication, enumerating users, reading attributes that should not be accessible or faking the result of a permission check. It belongs to the same injection family as SQL injection, although the target interpreter is different, and it is catalogued as CWE-90.
The essentials. LDAP injection arises from mixing code (the filter syntax) and data (the user input) in the same string. The correct defense is not a blocklist of characters, but escaping the input according to RFC 4515 for filters and RFC 4514 for distinguished names (DN), preferably through a framework API that does it for you. The remaining layers (allowlist validation, least privilege on the bind account, directory ACLs) reinforce that separation, but do not replace it.
How LDAP injection works: filter manipulation
LDAP (Lightweight Directory Access Protocol) is the protocol used by corporate directories such as Active Directory, OpenLDAP or 389 Directory Server to store users, groups and resources. Many applications query it to authenticate credentials, look people up or resolve group membership. The problem appears when the application builds its filter by concatenating user data: the directory server cannot tell what the programmer wrote apart from what the attacker supplied, and interprets the whole string as a single expression.
LDAP filter syntax
LDAP search filters follow RFC 4515 and use prefix (Polish) notation inside parentheses. A simple comparison is (uid=ana). Logical operators are placed in front of the conditions they group:
(&(cond1)(cond2))is the conjunction (AND): all must hold.(|(cond1)(cond2))is the disjunction (OR): at least one must hold.(!(cond))is the negation (NOT).- The asterisk
*is a wildcard that matches any sequence of characters.
The characters with special meaning inside a filter are *, (, ), \ and the null byte. If those characters reach the filter unescaped from user input, the attacker can rewrite the structure of the filter at will.
Authentication bypass
Consider a login that validates credentials against the directory by building the filter through concatenation:
# VULNERABLE: input is concatenated inside the LDAP filter
user = request.form["user"]
pwd = request.form["pass"]
ldap_filter = "(&(uid=" + user + ")(userPassword=" + pwd + "))"
conn.search("ou=people,dc=company,dc=com", ldap_filter)
If the attacker sends admin)(&) as the username and any password, the resulting string is:
(&(uid=admin)(&))(userPassword=x))
Many client libraries process the first well formed filter, (&(uid=admin)(&)), and discard the rest. The (&) component is the absolute true filter defined in RFC 4526: it always evaluates to true. The effect is that the filter reduces to "the uid is admin", ignoring the password check entirely. It is the LDAP equivalent of the classic ' OR '1'='1 in SQL.
The wildcard variant is even simpler: sending * as the username returns the first entry in the branch, and on poorly designed forms it is equivalent to authenticating as an arbitrary user. When the filter has the form (&(uid=USER)(userPassword=PWD)), injecting into both fields also lets the attacker close and reopen logical groups to neutralize the password condition.
Types of attack
Data extraction and blind injection
Even when the application shows no direct results, the attacker can infer information by observing behaviour, just as in blind SQL injection. The technique relies on wildcards and boolean operators to ask about a value character by character. For example, to guess the email of the admin account, filters like these are sent:
(&(uid=admin)(mail=a*))
(&(uid=admin)(mail=b*))
(&(uid=admin)(mail=ad*))
Each request produces a distinct observable response (a login that works or fails, a message that appears or disappears, a different load time) depending on whether the condition is true or false. With enough requests the full attribute is reconstructed.
Tampering with authorization logic
When the application decides permissions by querying group membership, a vulnerable filter lets the attacker fake that decision. If the code checks (&(uid=USER)(memberOf=cn=admins,ou=groups,dc=company,dc=com)), injecting a condition that closes the AND and adds an absolute true OR makes the group check stop being binding, which amounts to a logical privilege escalation.
LDAP injection versus SQL injection
Both share the root cause (mixing code and data in a string) and the OWASP category, but they differ in details that shape exploitation and defense. LDAP uses prefix notation with parentheses, not an infix syntax with keywords, and has no comment character equivalent to SQL's --: the attacker does not "comment out" the rest of the filter but relies on the client library ignoring whatever is left after the first valid filter. LDAP also has two injection contexts with different escaping, the search filter (RFC 4515) and the distinguished name or DN (RFC 4514), and confusing them leaves an exploitable gap. That is why the defense cannot be copied verbatim from command injection prevention: the principle is the same, but the implementation depends on the LDAP context.
Detection: ldapsearch, Burp and manual payloads
There is no tool as mature as sqlmap for LDAP, so detection relies mostly on manual testing guided by the OWASP methodology (WSTG-INPV-06, LDAP Injection Testing). The usual flow combines request interception with Burp Suite and later validation with the OpenLDAP ldapsearch client:
# Reproduce a suspicious filter against the directory to confirm the finding
ldapsearch -x -H ldap://directory.company.com -b "ou=people,dc=company,dc=com" "(&(uid=admin)(&))"
The initial probing payloads are simple: a *, a stray parenthesis ), the sequence )(, or the absolute true filter (&). If entering an unclosed ( makes the application return a directory syntax error, that is a strong sign the input reaches the filter unescaped. From there, Burp Intruder helps automate blind character by character extraction. It is worth testing every entry point, including headers, JSON fields and search parameters, because any of them can end up in a filter.
Prevention: context aware escaping and least privilege (CWE-90)
Escaping according to RFC 4515 and RFC 4514
The defense that addresses the root cause is escaping the input before inserting it into the filter, using the language function designed for that instead of a homemade replacement. In a filter, special characters are replaced by their escape sequence (* becomes \2a, ( becomes \28, ) becomes \29, \ becomes \5c). Almost every ecosystem includes a specific utility:
- PHP:
ldap_escape($value, "", LDAP_ESCAPE_FILTER)andLDAP_ESCAPE_DNfor the DN. - Python:
ldap.filter.escape_filter_chars()in python-ldap, orescape_filter_chars()in ldap3. - Java:
LdapEncoder.filterEncode()from Spring LDAP, orencodeForLDAP()andencodeForDN()from OWASP ESAPI. - .NET: context aware escaping when building filters with
System.DirectoryServices.Protocols.
The rule is the same as with SQL injection: no user data should ever be concatenated raw inside a filter. And it must be escaped for the correct context, because the set of special characters in a filter does not match the one in a DN.
Authentication by bind, not by password comparison
A common design mistake is placing the password inside the filter and comparing the userPassword attribute. Besides exposing a second injection point, it is insecure for other reasons (the attribute is usually hashed). The correct pattern separates the two operations: first the user is searched by identifier to obtain their DN, escaping the input, and then an authenticated bind is performed with that DN and the supplied password. The result of the bind decides whether the credentials are valid, without the password ever reaching a search filter.
Allowlist validation, ACLs and least privilege
Escaping is complemented by additional layers. Allowlist validation restricts each field to its expected format (an identifier that only accepts ^[a-zA-Z0-9._-]+$ rules out every metacharacter). Directory ACLs should limit which attributes the application account can read, so an injection does not turn into a mass data leak. And the service account that performs the bind should hold least privilege: read only over the strictly necessary branch, with no write access to sensitive attributes. This layered reasoning is the same we apply in Active Directory pentesting.
Real cases and fit with OWASP
LDAP injection is not a theoretical problem. CVE-2017-14596 affected Joomla in versions 1.5 through 3.7.5: the LDAP authentication plugin built the filter without sanitizing input, which allowed a blind injection to extract directory credentials character by character. The pattern repeats in admin panels, access portals and employee directories that delegate authentication to a corporate directory.
In the public catalogue, LDAP injection maps to CWE-90 (Improper Neutralization of Special Elements used in an LDAP Query) and is classified within A03:2021 Injection of the OWASP Top 10, the same category as SQL and command injection. You can see the full context in our analysis of the OWASP Top 10 2025 and in the review of the 5 most common web vulnerabilities.
Frequently asked questions
How does LDAP injection differ from SQL injection?
Both arise from mixing code and data and belong to the OWASP Injection category, but the target interpreter is different: SQL injection abuses the database engine and LDAP injection the directory server. LDAP uses prefix notation with parentheses, has no comment character and has two contexts with different escaping (filter under RFC 4515 and DN under RFC 4514), which changes both exploitation and remediation.
Is escaping special characters enough to prevent it?
Context aware escaping is the primary defense and addresses the root cause, as long as you use the right function for the right context (filter versus DN) and not an incomplete manual replacement. Even so, it should be reinforced with allowlist validation, with authentication by bind rather than password comparison in the filter, and with least privilege ACLs on the service account.
What is blind LDAP injection?
It is the variant where the application does not show the query results, but its behaviour changes depending on the injected filter. The attacker uses wildcards and boolean operators to ask about a value character by character (for example (&(uid=admin)(mail=a*))) and infers the information by observing which responses indicate true or false.
What tools are used to detect LDAP injection?
In manual testing, Burp Suite to intercept and modify parameters, and the OpenLDAP ldapsearch client to reproduce and confirm the filter against the directory. Burp Intruder automates blind extraction. The reference methodology is the OWASP Web Security Testing Guide, specifically test WSTG-INPV-06.
Related resources
- What is SQL injection: attack and prevention
- OS command injection
- Active Directory pentesting: tier model
- OWASP Top 10 2025: business vulnerabilities
- The 5 most common web vulnerabilities
- What is Burp Suite in web pentesting
Auditing LDAP injection with Secra
Secra performs web application audits aligned with the OWASP Top 10 and the OWASP Web Security Testing Guide. We test every entry point that reaches a directory (login forms, people search, group checks) for direct and blind LDAP injection, confirm findings manually with ldapsearch and verify that escaping is applied in the correct context, that authentication is resolved with a bind rather than by comparing the password in the filter, and that the service account operates with least privilege.
We deliver findings prioritised by impact, reproducible proofs of concept and hardening recommendations specific to your directory architecture. If you need a professional review, explore our web and mobile audit service or write to us through our contact page to plan an audit adapted to the scope of your platform.
About the author
Secra Solutions team
Ethical hackers with OSCP, OSEP, OSWE, CRTO, CRTL and CARTE certifications, 7+ years of experience in offensive cybersecurity, and authors of CVE-2025-40652 and CVE-2023-3512.

