Description
LDAP filter injection occurs when untrusted data is inserted into a search filter without RFC 4515 escaping. An attacker can add filter operators or wildcards to change its conditions, potentially bypassing authentication or retrieving directory entries the application did not intend to expose.
Potential impact
- User-search or authentication conditions may be bypassed.
- Sensitive directory information, such as users, groups, and email addresses, may be disclosed.
- Broad or expensive filters may increase LDAP server load. The actual impact depends on directory access controls and search limits.
Remediation
- Keep the filter structure and attribute names fixed. Insert untrusted data only as assertion values.
- With python-ldap, escape each assertion value using
ldap.filter.escape_filter_chars, or pass a trusted, fixed template and a separate value list toldap.filter.filter_format. Do not construct its first argument, the template, from external input. - With ldap3, escape each assertion value using
ldap3.utils.conv.escape_filter_charsbefore inserting it into a search filter. - Search-filter escaping and DN escaping are different. When building a DN, apply
ldap.dn.escape_dn_charsorldap3.utils.dn.escape_rdnto the relevant RFC 4514 values. - Apply business allow-lists and length limits where appropriate, and bind with a least-privileged account. These controls complement context-specific escaping; they do not replace it.
Examples
Before
python
import ldap
from flask import request
def bad_search():
conn = ldap.initialize("ldaps://directory.example.com")
username = request.args.get("user")
query = "(uid=%s)" % username
return conn.search_s("ou=users,dc=example,dc=com", ldap.SCOPE_SUBTREE, query)
After
python
import ldap
from ldap.filter import filter_format
from flask import request
def safe_search():
conn = ldap.initialize("ldaps://directory.example.com")
username = request.args.get("user")
query = filter_format("(uid=%s)", [username])
return conn.search_s("ou=users,dc=example,dc=com", ldap.SCOPE_SUBTREE, query)
Explanation:
- Before: User input is inserted directly into the LDAP filter string.
- After: The application fixes the filter template, and
filter_formatapplies RFC 4515 escaping to the assertion values supplied separately.
Implementation considerations
Filter escaping does not replace TLS verification, bind-account permissions, or required-input validation. These excerpts focus on constructing the search filter; prepare those settings separately. When building a DN or another LDAP expression, use escaping appropriate to that context.