【问题标题】:Parse ldap filter to escape special characters解析 ldap 过滤器以转义特殊字符
【发布时间】:2015-09-27 08:49:00
【问题描述】:

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 转义规则将它们分开并转义。
  2. 在字符串的其余部分搜索属性值中的特殊字符,如果它们尚未转义,则按照通用过滤器转义规则对其进行转义。
  3. 将结果组合为新的转义过滤器并将其传递。

Java 原生 javax.naming.ldap.Rdn 可以转义 dn 值,但不是幂等的。至于其他任务,到目前为止,我一直无法找到可以让我完成它们的库。

现在我倾向于认为转义 ldap 过滤器的工作应该由服务的用户而不是服务本身来完成,因为服务很难从实际值中分辨出转义.此外,在没有经过良好测试的库的情况下解析复杂的字符串(例如 ldap 过滤器)在我看来很容易出错。

关于如何解决这个问题的任何想法?这个任务可以自动化吗?

【问题讨论】:

  • 这真的需要你通过{0}机制提供参数吗?

标签: java regex parsing active-directory ldap


【解决方案1】:

为了转义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

【讨论】:

    【解决方案2】:

    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&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;
    }
    

    【讨论】:

    • / 字符呢?
    • 根据本站,正斜杠不需要转义:social.technet.microsoft.com/wiki/contents/articles/…
    • 当它是 DN 的一部分时,它肯定需要转义。这就是把我带到这里的原因
    • 是的...如果您滚动到我发送的链接的顶部,请查看“Active Directory”和“ADSI”部分。他们解释了 DN 需要转义的内容,并且只有在使用 ADSI 接口时才需要转义正斜杠。 (为了安全起见,如果你逃得太多,希望它不会受到伤害。)我发布的代码仅用于转义过滤器中使用的字符,而不是 DN。 (咆哮:恕我直言,所有这些不同的转义规则都表明协议设计很脆弱,但现在为时已晚。)
    【解决方案3】:

    如果调用者提供的 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 过滤器注入问题,但它不需要调用者处理转义,调用者只需要提供一个过滤器模板和一个值列表。

    通常,应优先使用库工具进行转义而不是自定义代码,以确保正确处理所有情况。其他两个答案说明了这一点。

    【讨论】:

      【解决方案4】:

      这是我的:

          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();
          }
      

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          猜你喜欢
          • 2021-01-02
          • 1970-01-01
          • 1970-01-01
          • 2014-06-06
          • 2022-12-11
          • 1970-01-01
          • 1970-01-01
          • 2011-05-03
          • 1970-01-01
          相关资源
          最近更新 更多