Parse ldap filter to escape special characters

10,033

Solution 1

For escaping LDAP filters, I relied on this page to write the code below: http://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx#Special_Characters

String LdapEscape(String ldap)
{
    if(ldap == null) return "";
    return ldap.replace("\\", "\\5C").replace("*", "\\2A").replace("(", "\\28").replace(")", "\\29").replace("\000", "\\00");
}

The most important thing to keep in mind here is that replacing \ with \5C must happen first so that you don't double escape any characters. Otherwise it's very straightforward; there aren't any special tricks to watch out for.

I'd like to point out that this is meant to escape individual values placed in LDAP filters, not the entire LDAP filter. However if you wanted, you could use that function to escape something like this so it can be searched for:

LdapEscape("(!(sn=m*))"); // \28!\28sn=m\2A\29

Solution 2

The answer by Pluto is great and concise, but non-ASCII UTF-8 characters (e.g. é, á, ö, etc.) need special handling too. Here's my verbose solution.

/** 
 * Filter components need to escape special chars.
 * Note that each piece of the filter needs to be escaped, 
 * not the whole filter expression, for example:
 * 
 * "(&(cn="+ esc("Admins") +")(member="+ esc("CN=Doe\\, Jöhn,OU=ImPeople,DC=ds,DC=augur,DC=com") +"))"
 * 
 * @see Oracle Directory Server Enterprise Edition 11g Reference doc
 * @see http://docs.oracle.com/cd/E29127_01/doc.111170/e28969/ds-ldif-search-filters.htm#gdxoy
 * @param s A String field within the search expression
 * @return The escaped string, safe for use in the search expression.
 */
public static String esc(String s)
{
    if(s == null) return "";
    StringBuilder sb = new StringBuilder(s.length());
    for (byte c : s.getBytes(StandardCharsets.UTF_8))
    {
        if (c=='\\') { sb.append("\\5c"); }
        else if (c=='*') { sb.append("\\2a"); }
        else if (c=='(') { sb.append("\\28"); }
        else if (c==')') { sb.append("\\29"); }
        else if (c==0) { sb.append("\\00"); }
        else if ((c&0xff)>127) { sb.append("\\").append(to2CharHexString((c&0xff))); } // UTF-8's non-7-bit characters, e.g. é, á, etc...
        else { sb.append((char)c); }
    }
    return sb.toString();
}

/** 
 * @return The least significant 16 bits as a two-character hex string, 
 * padded by a leading '0' if necessary.
 */
public static String to2CharHexString(int i)
{
    String s = Integer.toHexString(i & 0xff);
    if (s.length()==1) return "0"+s;
    else return s;
}
Share:
10,033
Robert Mugattarov
Author by

Robert Mugattarov

Updated on June 21, 2022

Comments

  • Robert Mugattarov
    Robert Mugattarov almost 2 years

    An ejb service takes a ldap filter as string and returns a result from ActiveDirectory.

    The problem is that sometimes attribute values contain special characters that need to be escaped for the entire filter as specified here:
    https://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx
    and for distinguished name attibute values as specified here:
    https://msdn.microsoft.com/en-us/library/aa366101(v=vs.85).aspx
    In order to accomplish this the service must do the following:

    1. Analyze the string for dn values, separate them and escape them as per dn escape rules if they are not already escaped.
    2. Search the remainder of the string for special characters in attribute values and escape them as per general filter escape rules if they are not already escaped.
    3. Combine the results as the new escaped filter and pass it on.

    Java native javax.naming.ldap.Rdn escapes dn values all right but is not idempotent. As for the other tasks, so far I have been unable to find a library that would allow me to accomplish them.

    Right now I am inclined to think that the job of escaping the ldap filter should be done by the user of the service rather than by the service itself as it is very hard for the service to tell escapes from actual values. Also, parsing a complex string such as a ldap filter without a well tested library seems to me error prone.

    Any ideas on how to solve this? Can this task be automated at all?

  • Nicholas DiPiazza
    Nicholas DiPiazza almost 6 years
    what about the / character?
  • Chris Janicki
    Chris Janicki almost 6 years
    Per this site, the forward slash does not need to be escaped: social.technet.microsoft.com/wiki/contents/articles/…
  • Nicholas DiPiazza
    Nicholas DiPiazza almost 6 years
    it definitely needs escaped when it's part of a DN. that's what brought me here anyways
  • Chris Janicki
    Chris Janicki almost 6 years
    Yes... If you scroll to the top of the link I sent, check out the "Active Directory" and "ADSI" sections. They explain what needs to be escaped for DNs, and that the forward slash needs to be escaped only when you use an ADSI interface. (Hopefully it can't hurt if you escape too much, to be safe.) The code I posted was only for escaping characters used in a filter, not a DN. (Rant: IMHO, all these varying escape rules signify a fragile protocol design, but it's too late now.)