Функция PHP ldap_add для экранирования специальных символов ldap в синтаксисе DN - PullRequest
12 голосов
/ 19 декабря 2011

Я пытаюсь добавить некоторых пользователей в мою базу данных Ldap, но у меня появляются некоторые ошибки (неверный синтаксис dn), когда я использую некоторые специальные символы, такие как ",.".Мне нужна функция, которая экранирует все символы.Я пробую preg_quote, но в некоторых случаях я получаю некоторые ошибки.

Заранее спасибо

Код:

$user = 'Test , Name S.L';

    if(!(ldap_add($ds, "cn=" . $user . ",".LDAP_DN_BASE, $info))) {

            include 'error_new_account.php';

    }

Ответы [ 4 ]

26 голосов
/ 19 декабря 2011

РЕДАКТИРОВАТЬ Янв 2013: добавлена ​​поддержка экранирования начальных / конечных пробелов в строках DN, для RFC 4514 .Спасибо Eugenio за указание на эту проблему.

EDIT 2014: Я добавил эту функцию в PHP 5.6 .Приведенный ниже код теперь заменяет собой более раннюю версию PHP.

if (!function_exists('ldap_escape')) {
    define('LDAP_ESCAPE_FILTER', 0x01);
    define('LDAP_ESCAPE_DN',     0x02);

    /**
     * @param string $subject The subject string
     * @param string $ignore Set of characters to leave untouched
     * @param int $flags Any combination of LDAP_ESCAPE_* flags to indicate the
     *                   set(s) of characters to escape.
     * @return string
     */
    function ldap_escape($subject, $ignore = '', $flags = 0)
    {
        static $charMaps = array(
            LDAP_ESCAPE_FILTER => array('\\', '*', '(', ')', "\x00"),
            LDAP_ESCAPE_DN     => array('\\', ',', '=', '+', '<', '>', ';', '"', '#'),
        );

        // Pre-process the char maps on first call
        if (!isset($charMaps[0])) {
            $charMaps[0] = array();
            for ($i = 0; $i < 256; $i++) {
                $charMaps[0][chr($i)] = sprintf('\\%02x', $i);;
            }

            for ($i = 0, $l = count($charMaps[LDAP_ESCAPE_FILTER]); $i < $l; $i++) {
                $chr = $charMaps[LDAP_ESCAPE_FILTER][$i];
                unset($charMaps[LDAP_ESCAPE_FILTER][$i]);
                $charMaps[LDAP_ESCAPE_FILTER][$chr] = $charMaps[0][$chr];
            }

            for ($i = 0, $l = count($charMaps[LDAP_ESCAPE_DN]); $i < $l; $i++) {
                $chr = $charMaps[LDAP_ESCAPE_DN][$i];
                unset($charMaps[LDAP_ESCAPE_DN][$i]);
                $charMaps[LDAP_ESCAPE_DN][$chr] = $charMaps[0][$chr];
            }
        }

        // Create the base char map to escape
        $flags = (int)$flags;
        $charMap = array();
        if ($flags & LDAP_ESCAPE_FILTER) {
            $charMap += $charMaps[LDAP_ESCAPE_FILTER];
        }
        if ($flags & LDAP_ESCAPE_DN) {
            $charMap += $charMaps[LDAP_ESCAPE_DN];
        }
        if (!$charMap) {
            $charMap = $charMaps[0];
        }

        // Remove any chars to ignore from the list
        $ignore = (string)$ignore;
        for ($i = 0, $l = strlen($ignore); $i < $l; $i++) {
            unset($charMap[$ignore[$i]]);
        }

        // Do the main replacement
        $result = strtr($subject, $charMap);

        // Encode leading/trailing spaces if LDAP_ESCAPE_DN is passed
        if ($flags & LDAP_ESCAPE_DN) {
            if ($result[0] === ' ') {
                $result = '\\20' . substr($result, 1);
            }
            if ($result[strlen($result) - 1] === ' ') {
                $result = substr($result, 0, -1) . '\\20';
            }
        }

        return $result;
    }
}

Итак, вы бы сделали:

$user = 'Test , Name S.L';
$cn = ldap_escape($user, '', LDAP_ESCAPE_DN);
if (!ldap_add($ds, "cn={$cn}," . LDAP_DN_BASE, $info)) {
    include 'error_new_account.php';
}
2 голосов
/ 27 апреля 2015

Просто наперед, если вы еще не работали с PHP 5.6, вы можете отразить точную функцию PHP 5.6 ldap_escape(), используя методы, которые я создал ниже, имейте в виду, что это предназначено для использования в классе.Приведенный выше ответ не работает точно так же, как функция ldap_escape, так как в нем не экранируются все символы в шестнадцатеричную строку, если не заданы флаги, так что это было бы более подходящим для удаления замены для более ранних версийPHP, объектно-ориентированным способом.

Я задокументировал каждую строку для более легкого понимания того, что происходит.Прокрутите вниз для вывода.

Методы (Совместимо с PHP 5 или выше):

/**
 * Escapes the inserted value for LDAP.
 *
 * @param string $value The value to escape
 * @param string $ignore The characters to ignore
 * @param int $flags The PHP flag to use
 *
 * @return bool|string
 */
public function escapeManual($value, $ignore = '*', $flags = 0)
{
    /*
     * If a flag was supplied, we'll send the value
     * off to be escaped using the PHP flag values
     * and return the result.
     */
    if($flags) {
        return $this->escapeWithFlags($value, $ignore, $flags);
    }

    // Convert ignore string into an array
    $ignores = str_split($ignore);

    // Convert the value to a hex string
    $hex = bin2hex($value);

    /*
     * Separate the string, with the hex length of 2,
     * and place a backslash on the end of each section
     */
    $value = chunk_split($hex, 2, "\\");

    /*
     * We'll append a backslash at the front of the string
     * and remove the ending backslash of the string
     */
    $value = "\\" . substr($value, 0, -1);

    // Go through each character to ignore
    foreach($ignores as $charToIgnore)
    {
        // Convert the characterToIgnore to a hex
        $hexed = bin2hex($charToIgnore);

        // Replace the hexed variant with the original character
        $value = str_replace("\\" . $hexed, $charToIgnore, $value);
    }

    // Finally we can return the escaped value
    return $value;
}

/**
 * Escapes the inserted value with flags. Supplying either 1
 * or 2 into the flags parameter will escape only certain values
 *
 *
 * @param string $value The value to escape
 * @param string $ignore The characters to ignore
 * @param int $flags The PHP flag to use
 * @return bool|string
 */
public function escapeWithFlags($value, $ignore = '*', $flags = 0)
{
    // Convert ignore string into an array
    $ignores = str_split($ignore);

    $escapeFilter = ['\\', '*', '(', ')'];
    $escapeDn = ['\\', ',', '=', '+', '<', '>', ';', '"', '#'];

    switch($flags)
    {
        case 1:
            // Int 1 equals to LDAP_ESCAPE_FILTER
            $escapes = $escapeFilter;
            break;
        case 2:
            // Int 2 equals to LDAP_ESCAPE_DN
            $escapes = $escapeDn;
            break;
        case 3:
            // If both LDAP_ESCAPE_FILTER and LDAP_ESCAPE_DN are used
            $escapes = array_merge($escapeFilter, $escapeDn);
            break;
        default:
            // Customize your own default return value
            return false;
    }

    foreach($escapes as $escape)
    {
        // Make sure the escaped value isn't inside the ignore array
        if( ! in_array($escape, $ignores))
        {
            $hexed = chunk_split(bin2hex($escape), 2, "\\");

            $hexed = "\\" . substr($hexed, 0, -1);

            $value = str_replace($escape, $hexed, $value);
        }
    }

    return $value;
}

Тесты (имейте в виду, что константы LDAP_ESCAPE доступны только вPHP 5.6):

// Value to escape
$value = 'testing=+<>"";:#()*\x00';

$php = ldap_escape($value, $ignore = '*');

$man = $this->escapeManual($value, $ignore = '*');

echo $php; // \74\65\73\74\69\6e\67\3d\2b\3c\3e\22\22\3b\3a\23\28\29*\5c\78\30\30
echo $man; // \74\65\73\74\69\6e\67\3d\2b\3c\3e\22\22\3b\3a\23\28\29*\5c\78\30\30


$php = ldap_escape($value, $ignore = '*', LDAP_ESCAPE_DN);

$man = $this->escapeManual($value, $ignore = '*', LDAP_ESCAPE_DN);

echo $php; // testing\3d\2b\3c\3e\22\22\3b:\23()*\5cx00
echo $man; // testing\3d\2b\3c\3e\22\22\3b:\23()*\5cx00


$php = ldap_escape($value, $ignore = '*', LDAP_ESCAPE_FILTER);

$man = $this->escapeManual($value, $ignore = '*', LDAP_ESCAPE_FILTER);

echo $php; // testing=+<>"";:#\28\29*\5cx00
echo $man; // testing=+<>"";:#\28\29*\5cx00

Ссылка на Github Gist: https://gist.github.com/stevebauman/0db9b5daa414d60fc266

2 голосов
/ 06 мая 2014

PHP 5.6 Beta выпущена ldap_escape() функция недавно и она действует, однако в настоящее время эта версия не готова к работе, вы можете очень использовать ее в целях разработкина данный момент.

1 голос
/ 19 декабря 2011

Эти символы должны быть экранированы, чтобы быть частью данных отличительного имени или относительного отличительного имени. Экранируйте символ (как во всех LDAP) с помощью шестнадцатеричной цифры с обратной косой чертой, такой как \2a. Все остальное не будет соответствовать документам органа стандартизации. См. RFC4514 для получения более конкретной информации о строковом представлении отличительных имен.

...