LDAP injection is a vulnerability that lets an attacker change the meaning of a directory query by typing filter syntax into an ordinary input field. The application pastes that text straight into an LDAP search filter, the directory service runs the finished string exactly as written, and a password check quietly stops checking anything. MITRE tracks the weakness as CWE-90. This guide covers how the attack works, the characters that make it possible, the three forms it takes, and the code and edge controls that shut it down. The shortest working payload is smaller than most passwords.
Key Takeaways
- LDAP injection happens when user input is concatenated into a search filter or a distinguished name without escaping, so the input becomes query logic instead of data.
- A payload as short as *)(uid=*))(|(uid=* can make an authentication filter always true.
- Three forms appear in practice: filter logic injection, blind extraction, and distinguished name injection.
- Escaping filter values to RFC 4515 and distinguished names to RFC 4514 removes the flaw at its source.
- A least-privilege bind account and disabled anonymous bind limit what a surviving payload can read or change.
- Edge controls block known payload shapes and throttle the request volume blind extraction depends on.
How an LDAP injection attack works
An LDAP injection attack works by inserting filter syntax into an input field that the application copies into a directory query. The directory service parses the finished string and cannot tell which characters came from the developer and which came from the visitor, so it runs all of them. The visitor stops filling in the form and starts rewriting it.
LDAP search filters use prefix notation, described in RFC 4515. A login check usually compares two attributes at once, the account name and the password, joined by an AND operator.
Read the last line closely. The AND group now matches any user ID, twice over, and the password clause sits in an OR branch that never needs to be true. The directory returns a result, and the visitor is logged in.

This is not a museum piece. CVE-2021-23335 covers the npm package is-user-valid, where every published version carried an LDAP injection flaw ending in authentication bypass. NVD records CVE-2023-4501 in OpenText Enterprise Server, where LDAP-based authentication in some configurations accepted any valid username regardless of the password.
The mechanism belongs to a much wider family. Every injection attack shares one root cause: a string mixing trusted structure with untrusted data, handed to an interpreter that sees only one string.
Which characters turn user input into query logic
Five characters carry structural meaning inside an LDAP filter value, and each has a defined escape form. Neutralize those five and a visitor can type anything at all without changing the shape of the query.
| Character | What it does inside a filter | Escaped form |
|---|---|---|
| * | Wildcard that matches any stored value | \2a |
| ( | Opens a new filter clause | \28 |
| ) | Closes the clause the developer opened | \29 |
| \ | Escape prefix that hides the next character | \5c |
| NUL | Terminates the filter string early | \00 |

Here is the part most guides skip. The AND and OR operators, & and |, are absent from that escape list, and the omission looks like an oversight. It is not. Those operators carry meaning only directly after an opening parenthesis, so an attacker who cannot smuggle in a parenthesis cannot use them.
Distinguished names follow a separate rule set in RFC 4514. There, the dangerous characters are the comma, plus sign, quotation mark, backslash, angle brackets, semicolon, a leading hash, and spaces at either end of a value.
The three forms LDAP injection takes
LDAP injection shows up in three shapes, separated by what the attacker manipulates and what they get back. All three start from the same concatenation bug.
- Filter logic injection rewrites the clause structure so the query becomes always true, usually to bypass a login form.
- Blind injection sends a probe such as admin)(userPassword=a* and watches whether the page behaves differently. Repeated across the alphabet, stored values come out one character at a time.
- Distinguished name injection targets the DN or the search base instead of the filter, redirecting a lookup into a branch of the tree the account was never meant to read.

Blind injection is the one that gets underestimated. It produces no stack trace and no visible error, so it rarely triggers an alert, and it works fine against an application that shows only a generic failure page.
How LDAP injection compares with other injection flaws
The interpreter changes, the punctuation changes, the mistake does not.
| Attack | Interpreter targeted | Payload style | Typical outcome |
|---|---|---|---|
| LDAP injection | Directory service such as Active Directory or OpenLDAP | Filter metacharacters and unbalanced parentheses | Authentication bypass and directory disclosure |
| SQL injection | Relational database | Quotes, comments, and appended statements | Records read from tables outside the query |
| NoSQL | Document database | JSON operator objects | Query logic changed without a single quote |
| XXE | XML parser | External entity declarations | Local file read and server-side requests |
Harden one and the work transfers. A union-based SQL injection appends a second result set to a relational query. A NoSQL injection swaps a plain string for an operator object such as $ne. An XXE attack abuses entity declarations in an XML document. LDAP injection does the same to a search filter, and one pattern fixes all four.
What a successful LDAP injection costs you
The cost is set by what the directory stores. LDAP is rarely a side system: it holds usernames, password attributes, group memberships, and service account details for the whole company. Compromise the query that reads it and you compromise the authority that decides who gets in.
- Authentication bypass. An always-true filter turns a login form into a door with no lock.
- Directory disclosure. Wildcard filters return email addresses, distinguished names, and any attribute the bind account can read.
- Privilege escalation. A rewritten DN or an injected group membership check promotes an ordinary account.
- Content modification. Where the application writes to the directory, injected syntax changes what gets written.
You might be thinking the directory sits on an internal network, which caps the damage. Yes, but the directory decides who counts as internal, so a bypass there rewrites that boundary. A web application firewall in front of the login endpoint shortens the window an attacker gets, though only the code fix ends the vulnerability.
How to prevent LDAP injection in application code
Prevention comes down to making sure user input can never be read as syntax. Work through these in order.
- Escape every value with the encoder built for LDAP filters. python-ldap ships escape_filter_chars; .NET provides Encoder.LdapFilterEncode, and Java projects can use OWASP ESAPI encodeForLDAP.
- Use the separate DN encoder for anything placed in a distinguished name. Filter escaping leaves the comma and plus sign live, which is exactly what DN injection needs.
- Validate against an allow list before escaping. If usernames are alphanumeric with a dot, reject everything else.
- Never build the search base or the scope from user input. Those belong in configuration.
- Bind with a least-privilege service account that reads only the attributes the feature needs. This decides whether a surviving payload returns four fields or forty.
- Disable anonymous and unauthenticated bind, and return one generic failure message. Filter syntax echoed back in an error page hands the attacker a free debugger.
Verify the password with a bind operation, not by comparing an attribute inside the filter. The OWASP LDAP Injection Prevention Cheat Sheet names the two causes behind most of these bugs: the shortage of parameterized LDAP interfaces, and how many applications use a directory to authenticate people.
How edge controls stop LDAP injection payloads
Edge controls sit in front of the application and reject payload shapes before any code touches them. They cannot remove the flaw, and they buy the time you need to ship the fix that does.

Managed rule sets flag unbalanced parentheses, wildcard sequences, and null bytes in parameters that should never contain them. Understanding how WAF rules evaluate a request helps, because input that looks fine to a login handler looks obviously wrong to a rule that knows the parameter holds a username.
Generic rules only reach so far. A customized WAF policy pins tighter conditions to the endpoints that query the directory, usually a short list: sign-in, password reset, user search, and directory lookup. Pair that with advanced firewall integration so directory ports stay closed to everything except the application tier.
Then there is volume. Blind extraction is not one clever request; it is hundreds differing by a single character, and rate limiting makes that pattern expensive long before the alphabet runs out. An attacker who needs four hours per hash goes looking for a softer target.
You might be thinking a strong edge policy makes escaping optional. It does not. Rules match on patterns, and a payload arriving in an unexpected encoding can walk past a signature. Treat the edge as the layer that catches what you have not fixed yet.
How to test an application for LDAP injection
Start with the fields that plausibly reach a directory: login, password reset, and user search. The OWASP Web Security Testing Guide describes the method, inserting characters such as parentheses, pipe, ampersand, and asterisk to see which break the query.
- Submit the metacharacters one at a time and watch for a server error or a changed response.
- Try a wildcard in a search box and count the results. A jump from a handful of matches to the whole directory is the signal.
- Check that error pages never echo filter text, and that a failed login and an unknown user return the same message and response time.
Final Thought on LDAP Injection
LDAP injection survives because directory queries look like configuration rather than code, so the concatenation that builds them escapes the scrutiny an SQL query attracts. The fix is unglamorous and reliable: escape values with the encoder matching the grammar you write into, validate against an allow list, and bind with an account that can barely see anything.
Layer the rest around that core. Edge filtering absorbs the noisy attempts, rate controls make patient extraction impractical, and tight bind privileges shrink whatever slips past. Start by searching your codebase for filter strings built by concatenation, because that one search usually finds every instance you have.
Common Questions About LDAP Injection
Is LDAP injection as severe as SQL injection?
It is often worse in practice. A database breach exposes the records in one application, while a directory breach exposes the credentials and group memberships governing access to many. Exploitation difficulty is comparable, and the blast radius is larger.
Does using Active Directory instead of OpenLDAP protect me?
No. The vulnerability lives in the application code that builds the filter, not in the directory product. Active Directory speaks the same filter grammar, so an unescaped username behaves the same against either server.
Does LDAPS or StartTLS prevent LDAP injection?
No. Encryption protects the query in transit. The injected payload is inside the query itself, so it arrives intact and the directory parses it exactly as before.