regex 解析ldap过滤器以转义特殊字符

mtb9vblg  于 2023-03-09  发布在  其他
关注(0)|答案(5)|浏览(443)

EJB服务将LDAP筛选器作为字符串,并从ActiveDirectory返回结果。
问题是,有时属性值包含特殊字符,需要对整个过滤器进行转义,如下所示:
https://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx
以及此处指定的可分辨名称属性值:
https://msdn.microsoft.com/en-us/library/aa366101(v=vs.85).aspx
为了实现此目的,服务必须执行以下操作:
1.分析字符串中的dn值,将它们分开,如果它们还没有转义,则按照dn转义规则进行转义。
1.在字符串的其余部分搜索属性值中的特殊字符,如果尚未转义,则按照常规过滤器转义规则对它们进行转义。
1.合并结果作为新的转义过滤器并传递它。
Java native javax.naming.ldap.Rdn可以正确地转义dn值,但不是幂等的。至于其他任务,到目前为止我还无法找到一个可以让我完成它们的库。
现在我倾向于认为转义ldap过滤器的工作应该由服务的用户来完成,而不是由服务本身来完成,因为服务很难区分转义符和实际值,而且,在没有经过良好测试的库的情况下解析一个复杂的字符串,比如ldap过滤器,在我看来很容易出错。
有什么办法解决这个问题吗?这个任务能完全自动化吗?

7y4bm7vi

7y4bm7vi1#

对于转义LDAP过滤器,我依赖此页面编写了以下代码: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");
}

这里要记住的最重要的一点是,必须先用\5C替换\,这样就不会对任何字符进行双转义,否则就非常简单了;没有什么特别的花招需要注意。
我想指出的是,这是为了转义LDAP过滤器中的单个值,而不是整个LDAP过滤器。但是,如果您愿意,您可以使用该函数来转义类似下面的内容,以便搜索:

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

vjrehmav2#

Pluto给出的答案很棒也很简洁,但非ASCII UTF-8字符(例如é、á、ö等)也需要特殊处理。

/** 
 * 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))); } // 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;
}
brccelvz

brccelvz3#

如果LDAP筛选器代表要用于查询的最终筛选器,则无法可靠地对调用方提供的LDAP筛选器中的值进行转义。请考虑调用方组装的以下筛选器:

String value = "*)(objectClass=*";
String filter = "(|(attr1=constvalue)(attr2=" + value + "))";
search(filter);

生成的过滤器匹配所有对象,因为代码没有转义value

(|(attr1=constvalue)(attr2=*)(objectClass=*))

无法基于最终滤波器转义value,因为无法再识别起始和结束位置。
为了解决这种模糊性和过滤器注入问题,必须在构建过滤器时转义输入值,而不是在构建过滤器之后。然而,这并不意味着调用者必须直接处理转义细节--这很容易出错,所以我不推荐这样做。
EJB处理转义所需要的只是知道所需的过滤器包括占位符(而不是实际值)和值列表。Java的DirContext已经提供了一种可以利用的机制。DirContext.search有一个filterExpr参数,表示可以包含占位符的过滤器模板,还有一个filterArgs参数,表示相应值的列表。
这样,上面的例子就变成了:

String value = "*)(objectClass=*";
String filter = "(|(attr1=constvalue)(attr2={0}))";
search(filter, new String[] { value });

search的实现:

dircontext.search(basedn, filter, valuearray, null);

这是我能想到的最好的折衷方案。它完全解决了LDAP过滤器注入问题,但它不需要调用者处理转义,调用者只需要提供一个过滤器模板和值列表。
一般来说,使用库工具进行转义应该比使用自定义代码更好,以确保所有情况都得到正确处理。另外两个答案说明了这一点。

v440hwme

v440hwme4#

我的答案是:

static public string LDAPEscape(string s)
    {
        StringBuilder sb = new StringBuilder(s.Length);
        int i;
        for (i = 0; i < s.Length; i++)
        {
            char c = s[i];
            if ("/*)(\\\0".IndexOf(c) >= 0)
                sb.Append('\\').Append(((uint)c).ToString("x2"));
            else
                sb.Append(c);
        }
        return sb.ToString();
    }
3bygqnnd

3bygqnnd5#

如果使用maven存储库apache-ldap-api作为LDAP客户端库,我们可以使用Util类FilterEncoder来处理LDAP搜索过滤器值中特殊字符的编码,如下所示

filter = "(attributeKey=" + FilterEncoder.encodeFilterValue(attributeValue) + ")";

相关问题