Java-декодирование закодированных фильтров LDAP перед передачей? Предотвращение 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)"

Последнее, что я могу отладить, это следующий код (начинается со строки 229 org.springframework.ldap.core.LdapTemplate):

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

Когда позже вызывается executeSearch(), я также могу убедиться, что строка фильтра содержит "(sAMAccountName=boi\2a)".

Я не могу продолжать отладку, так как у меня нет исходного кода для javax,naming.* или com.sun.jndi.ldap.* (поскольку вызывается com.sun.jndi.ldap.LdapCtx).

Однако, как только вызов возвращается из executeSearch(), WireShark сообщает мне, что пакет LDAP, содержащий searchRequest с фильтром "(sAMAccountName=boi*)" был передан (* больше не экранируется).

Я использовал аналогичную кодировку и использовал разные методы LdapTemplate, которые дали ожидаемый результат (я видел, как закодированный фильтр передается в WireShark), но я не могу объяснить, почему в случае, который я только что показал, значение декодируется перед передачей .

Помогите, пожалуйста, разобраться в ситуации. К счастью, я тот, кто неправильно понимает протокол LDAP.

Спасибо.

Отказ от ответственности: я разместил тот же вопрос на форумах Spring LDAP.

TL/DR: Почему com.sun.jndi.ldap.LdapCtx декодирует закодированные фильтры LDAP (например, от \2a до *) перед их передачей на сервер LDAP?

Обновление: Пробовал и наблюдал такое же поведение с IBM J9 JDK7.


person bug    schedule 17.10.2013    source источник


Ответы (2)


Хотя я не знаком с Spring LDAP, не похоже, что есть причина для беспокойства. Фильтры LDAP передаются не в виде открытого текста, а в двоичном кодировании, и в этом механизме нет необходимости в экранировании (и это было бы неправильно).

Возьмем в качестве примера "(sAMAccountName=boi*)". Как написано, этот фильтр является фильтром подстроки с subInitial компонентом "boi". Как вы указываете, если вы хотите, чтобы это был фильтр равенства, а не фильтр подстроки, тогда строковое представление должно быть "(sAMAccountName=boi\2a)". Однако двоичные кодировки для этих фильтров не используют никакого экранирования, а вместо этого используют тип BER ASN.1, чтобы различать фильтры подстроки и фильтры равенства.

Если вы хотите использовать "(sAMAccountName=boi*)" в качестве фильтра подстроки, то закодированное представление будет таким:

 a417040e73414d4163636f756e744e616d6530058003626f69

С другой стороны, если вы хотите использовать "(sAMAccountName=boi\2a)" в качестве фильтра равенства, кодировка будет следующей:

 a316040e73414d4163636f756e744e616d650404626f692a

Я не хочу вдаваться в полное объяснение кодировки, но «a4» в начале первого указывает, что это фильтр подстроки, а «a3» в начале второго указывает, что это фильтр. фильтр равенства.

Вы должны быть в состоянии проверить фактические байты, отправленные в WireShark. Вполне может быть, что WireShark неправильно обходит фильтр при создании строкового представления, но это будет проблемой самого WireShark. Сервер каталогов получает только двоичное представление, и трудно поверить, что сервер LDAP неправильно его интерпретирует.

person Neil Wilson    schedule 17.10.2013
comment
Право на. То, что я перехватываю, действительно является фильтром равенства (3). Я понимаю, что тогда не будет никакого поиска подстроки (и инъекции). Спасибо! (Для протокола, вы были правы: a316040e73414d4163636f756e744e616d650404626f692a) - person bug; 17.10.2013

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

person Matthias M    schedule 12.11.2014