3

I'm writing some python3 code using the ldap3 library and I'm trying to prevent LDAP-injection. The OWASP injection-prevention cheat sheet recommends using a safe/parameterized API(among other things). However, I can't find a safe API or safe method for composing search queries in the ldap3 docs. Most of the search queries in the docs use hard-coded strings, like this:

conn.search('dc=demo1,dc=freeipa,dc=org', '(objectclass=person)')

and I'm trying to avoid the need to compose queries in a manner similar to this:

conn.search(search, '(accAttrib=' + accName + ')')

Additionally, there seems to be no mention of 'injection' or 'escaping' or similar concepts in the docs. Does anyone know if this is missing in this library altogether or if there is a similar library for Python that provides a safe/parameterized API? Or has anyone encountered and solved this problem before?

A final point: I've seen the other StackOverflow questions that point out how to use whitelist validation or escaping as a way to prevent LDAP-injection and I plan to implement them. But I'd prefer to use all three methods if possible.

John Devitt
  • 690
  • 2
  • 8
  • 22
  • 3
    Have you not encountered the filter-with-arguments syntax? e.g. `'(accAttrib={0})'`, with a provided argument of `accName`? Similar to SQL prepared statements. Python should provide that in its API. – user207421 Nov 21 '17 at 05:38
  • Hadn't seen them before! Thanks! – John Devitt Nov 27 '17 at 14:18

2 Answers2

4

I was a little surprised that the documentation doesn't seem to mention this. However there is a utility function escape_filter_chars which I believe is what you are looking for:

from ldap3.utils import conv

attribute = conv.escape_filter_chars("bar)", encoding=None)
query = "(foo={0})".format(attribute)
conn.search(search, query)
Philip Couling
  • 13,581
  • 5
  • 53
  • 85
1

I believe the best way to prevent LDAP injection in python3 is to use the Abstraction Layer.

Example code:

# First create a connection to ldap to use.
# I use a function that creates my connection (abstracted here)
conn = self.connect_to_ldap()

o = ObjectDef('groupOfUniqueNames', conn)
query = 'Common Name: %s' % cn
r = Reader(conn, o, 'dc=example,,dc=org', query)
r.search()

Notice how the query is abstracted? The query would error if someone tried to inject a search here. Also, this search is protected by a Reader instead of a Writer. The ldap3 documentation goes through all of this.

Arash Hatami
  • 5,297
  • 5
  • 39
  • 59
byronicle
  • 11
  • 1