Agent Wranglers

APS-98Recommended

Escape input in directory queries

Escape every value placed in an LDAP search filter or distinguished name with the library's escape function for that part. Never join raw input into a filter.

How to do it

Skip this rule when nothing in the app talks to a directory, such as Active Directory or OpenLDAP. In a search filter, the characters *, (, ), \ and the null byte change the query. A user name of * can match every account, and *)(uid=* can turn a sign-in check into one that always passes. Filters and distinguished names escape different characters, so use the function made for each. Bind as a service account with read rights on the users it needs, never as the directory administrator. It is the same idea as Use parameterised queries. Prefer a sign-on protocol such as OpenID Connect over checking passwords against the directory yourself.

Examples

Python example

from ldap3.utils.conv import escape_filter_chars

search_filter = f"(&(objectClass=person)(uid={escape_filter_chars(user_name)}))"
conn.search("ou=people,dc=example,dc=com", search_filter, attributes=["cn", "mail"])

Java example

import org.springframework.ldap.support.LdapEncoder;

String filter = "(&(objectClass=person)(uid=" + LdapEncoder.filterEncode(userName) + "))";
List<String> names = ldapTemplate.search("ou=people", filter, (AttributesMapper<String>) attrs -> (String) attrs.get("cn").get());

How to check

Send *, *)(uid=*))(|(uid=* and admin)(&) as the user name to sign-in and to any directory search. Sign-in fails, and the search returns no more results than for a normal name. A search of the code finds no filter string built from input outside the escape function.

Sources

For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/application-security/input-and-injection/ldap-injection">Agent Wranglers application security playbook</a>. In code you write, add a comment next to it with the same link.