Java在传输之前解码编码的LDAP过滤器?防止LDAP注入

时间:2013-10-17 15:42:55

标签: java security spring-security ldap spring-ldap

我目前正在使用Spring LDAP Filter子句或通过LdapEncoder.filterEncode()来正确转义我的过滤器。

与此同时,我正在使用WireShark来捕获在本地计算机和LDAP服务器之间交换的数据包。

我似乎有问题。即使我正确地逃避了值(我已通过调试确认),它们也会通过网络无法转义。我还确认(通过调试)该值一直保持编码,直到它进入javax.naming.InitialContext。

以下是一个示例(请注意,我使用的是Spring LDAP 1.3.0,这些都发生在Oracle JDK 6u45和Oracle JDK 7u45上)。

在我自己的代码中,在服务层上,正在进行的调用是:

     String lMailAddress = (String) ldapTemplate.searchForObject("", new EqualsFilter(ldapUserSearchFilterAttribute, principal).encode(), new ContextMapper() {
                @Override
                public Object mapFromContext(Object ctx) {
                    DirContextAdapter lContext = (DirContextAdapter) ctx;
                    return lContext.getStringAttribute("mail");
                }});

此时,我可以确认过滤器上的encode()方法返回的字符串是“(sAMAccountName = boi \ 2a)”

我可以调试代码的最后一点是以下内容(从org.springframework.ldap.core.LdapTemplate第229行开始):

SearchExecutor se = new SearchExecutor() {
            public NamingEnumeration executeSearch(DirContext ctx) throws javax.naming.NamingException {
                return ctx.search(base, filter, controls);
            }
        };

稍后调用executeSearch()时,我还可以验证过滤器字符串是否包含“(sAMAccountName = boi \ 2a)”。

我无法进一步调试,因为我没有javax的源代码,命名。*或com.sun.jndi.ldap。*(因为正在调用com.sun.jndi.ldap.LdapCtx)。< / p>

但是,一旦调用从executeSearch()返回,WireShark就会通知我包含带有过滤器“(sAMAccountName = boi *)”的searchRequest的LDAP数据包已被传输(*不再被转义)。 / p>

我使用了类似的编码,并使用了不同的LdapTemplate方法,产生了我期望的结果(我看到编码的滤波器在WireShark中传输),但我无法解释为什么,在我刚刚暴露的情况下,值被解码在传播之前。

请帮助我了解情况。 Hpoefully,我是在这里没有正确理解LDAP协议的人。

感谢。

免责声明:我已向Spring LDAP论坛发布了同样的问题。

TL / DR:为什么com.sun.jndi.ldap.LdapCtx在将LDAP编码过滤器(如\ 2a转换为*)传输到LDAP服务器之前将其解码?

更新:尝试并观察了与IBM的J9 JDK7相同的行为。

2 个答案:

答案 0 :(得分:3)

虽然我不熟悉Spring LDAP,但听起来并不一定有理由担心。 LDAP过滤器不是以明文形式传输,而是以二进制编码传输,并且不需要在此机制中进行转义(这样做也不正确)。

我们以“(sAMAccountName = boi *)”为例。如上所述,此过滤器是子字符串过滤器,其subInitial组件为“boi”。正如您所指出的,如果您希望它是一个相等过滤器而不是子串过滤器,那么字符串表示必须是“(sAMAccountName = boi \ 2a)”。但是,这些过滤器的二进制编码不使用任何转义,而是使用ASN.1 BER类型来区分子字符串和相等过滤器。

如果你想要“(sAMAccountName = boi *)”作为子串过滤器,那么编码的表示将是:

 a417040e73414d4163636f756e744e616d6530058003626f69

另一方面,如果你想“(sAMAccountName = boi \ 2a)”作为相等过滤器,编码将是:

 a316040e73414d4163636f756e744e616d650404626f692a

编码的完整解释不是我想要进入的,但是第一个开头的“a4”表示它是子串滤波器,而第二个开头的“a3”表示这是一个相等的过滤器。

您应该能够验证WireShark中发送的实际字节数。很可能WireShark在生成字符串表示时没有正确地逃避过滤器,但这对WireShark本身来说是一个问题。目录服务器只获取二进制表示,很难相信LDAP服务器会误解它。

答案 1 :(得分:2)

OWASP建议对搜索字符串进行编码:

public static final String escapeLDAPSearchFilter(String filter) {
   StringBuffer sb = new StringBuffer(); // If using JDK >= 1.5 consider using StringBuilder
   for (int i = 0; i < filter.length(); i++) {
       char curChar = filter.charAt(i);
       switch (curChar) {
           case '\\':
               sb.append("\\5c");
               break;
           case '*':
               sb.append("\\2a");
               break;
           case '(':
               sb.append("\\28");
               break;
           case ')':
               sb.append("\\29");
               break;
           case '\u0000': 
               sb.append("\\00"); 
               break;
           default:
               sb.append(curChar);
       }
   }
   return sb.toString();

}

DN字符串的转义不同。请参阅以下链接。

https://www.owasp.org/index.php/Preventing_LDAP_Injection_in_Java