Проблемы с безопасным связыванием с Active Directory с использованием PHP - PullRequest
11 голосов
/ 10 марта 2011

Кажется, я не могу использовать php для безопасного связывания с Active Directory. Незашифрованные соединения работают нормально. Использование других клиентов может надежно связывать, например, подключение с использованием LDAPAdmin через SSL. В чем здесь проблема? Я пропускаю какой-нибудь модуль LDAP SSL? Как безопасно привязаться к серверу с помощью php?

Я заметил из phpinfo(), что в cURL есть поддержка ldap / ldaps - что является хорошим примером использования этого для выполнения безопасного связывания в php? Это жизнеспособный обходной путь?

phpinfo ();

ldap
LDAP Support    enabled
RCS Version     $Id: ldap.c 293036 2010-01-03 09:23:27Z sebastian $
Total Links     0/unlimited
API Version     3001
Vendor Name     OpenLDAP
Vendor Version  20421
SASL Support    Enabled 

Попытка привязки к серверу Active Directory с использованием PHP версии 5.3.2-1ubuntu4.7 из Ubuntu 10.04 repo

$username = 'user';
$password = 'passwd';
$account_suffix = '@example.com';
$hostnameSSL = 'ldaps://ldap.example.com:636';
$hostnameTLS = 'ldap.example.com';
$portTLS = 389;

ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7);

// Attempting fix from http://www.php.net/manual/en/ref.ldap.php#77553
putenv('LDAPTLS_REQCERT=never');

####################
# SSL bind attempt #
####################
// Attempting syntax from http://www.php.net/manual/en/function.ldap-bind.php#101445
$con =  ldap_connect($hostnameSSL);
if (!is_resource($con)) trigger_error("Unable to connect to $hostnameSSL",E_USER_WARNING);

// Options from http://www.php.net/manual/en/ref.ldap.php#73191
if (!ldap_set_option($con, LDAP_OPT_PROTOCOL_VERSION, 3))
{
    trigger_error("Failed to set LDAP Protocol version to 3, TLS not supported",E_USER_WARNING);
}
ldap_set_option($con, LDAP_OPT_REFERRALS, 0);

if (ldap_bind($con,$username . $account_suffix, $password)) die('All went well using SSL');
ldap_close($con);

####################
# TLS bind attempt #
####################
$con =  ldap_connect($hostnameTLS,$portTLS);
ldap_set_option($con, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($con, LDAP_OPT_REFERRALS, 0);
$encrypted = (ldap_start_tls($con));
if ($encrypted) ldap_bind($con,$username . $account_suffix, $password); // Unecrypted works, but don't want logins sent in cleartext
ldap_close($con);

#####################
# SASL bind attempt #
#####################
$con =  ldap_connect($hostnameTLS,$portTLS);
ldap_set_option($con, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($con, LDAP_OPT_REFERRALS, 0);
ldap_sasl_bind($con, NULL, $password, 'DIGEST-MD5', NULL, $username. $account_suffix);
ldap_close($con);

Все вышеперечисленное не помогает. Ошибки из журнала:

ldap_create
ldap_url_parse_ext(ldaps://ldap.example.com:636)
ldap_bind_s
ldap_simple_bind_s
ldap_sasl_bind_s
ldap_sasl_bind
ldap_send_initial_request
ldap_new_connection 1 1 0
ldap_int_open_connection
ldap_connect_to_host: TCP ldap.example.com:636
ldap_new_socket: 27
ldap_prepare_socket: 27
ldap_connect_to_host: Trying 1.1.1.1:636
ldap_pvt_connect: fd: 27 tm: -1 async: 0
ldap_open_defconn: successful
ldap_send_server_request
ldap_result ld 0x215380c0 msgid 1
wait4msg ld 0x215380c0 msgid 1 (infinite timeout)
wait4msg continue ld 0x215380c0 msgid 1 all 1
** ld 0x215380c0 Connections:
* host: ldap.example.com  port: 636  (default)
  refcnt: 2  status: Connected
  last used: Thu Mar 10 11:15:53 2011


** ld 0x215380c0 Outstanding Requests:
 * msgid 1,  origid 1, status InProgress
   outstanding referrals 0, parent count 0
  ld 0x215380c0 request count 1 (abandoned 0)
** ld 0x215380c0 Response Queue:
   Empty
  ld 0x215380c0 response count 0
ldap_chkResponseList ld 0x215380c0 msgid 1 all 1
ldap_chkResponseList returns ld 0x215380c0 NULL
ldap_int_select
read1msg: ld 0x215380c0 msgid 1 all 1
ldap_err2string
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP Warning:  ldap_bind() [<a href='function.ldap-bind'>function.ldap-bind</a>]: Unable to bind to server: Can't contact LDAP server in /..test.php on line 28
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP Stack trace:
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP   1. {main}() /..test.php:0
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP   2. ldap_bind() /..test.php:28
ldap_free_request (origid 1, msgid 1)
ldap_free_connection 1 1
ldap_free_connection: actually freed
ldap_create
ldap_err2string
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP Warning:  ldap_start_tls() [<a href='function.ldap-start-tls'>function.ldap-start-tls</a>]: Unable to start TLS: Not Supported in /..test.php on line 37
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP Stack trace:
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP   1. {main}() /..test.php:0
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP   2. ldap_start_tls() /..test.php:37
ldap_create
ldap_sasl_interactive_bind_s: user selected: DIGEST-MD5
ldap_err2string
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP Warning:  ldap_sasl_bind() [<a href='function.ldap-sasl-bind'>function.ldap-sasl-bind</a>]: Unable to bind to server: Not Supported in /..test.php on line 47
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP Stack trace:
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP   1. {main}() /..test.php:0
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP   2. ldap_sasl_bind() /..test.php:47

Глядя на ответ ssl:

>> openssl s_client -connect my.example.com:636 -prexit

(...)
SSL handshake has read 5732 bytes and written 443 bytes
---
New, TLSv1/SSLv3, Cipher is RC4-MD5
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : RC4-MD5
    Session-ID: 111111111111111111111111
    Session-ID-ctx: 
    Master-Key: AAAAAAAAAAAAAAAAAAAAA
    Key-Arg   : None
    Start Time: 1299071105
    Timeout   : 300 (sec)
    Verify return code: 20 (unable to get local issuer certificate)

Результаты 'strace php test.php':

    write(2, "  refcnt: 2  status: Connected\n", 31  refcnt: 2  status: Connected
    ) = 31
    write(2, "  last used: Tue Mar 15 10:59:19"..., 39  last used: Tue Mar 15 10:59:19 2011

    ) = 39
    write(2, "\n", 1
    )                       = 1
    write(2, "** ld 0x954e0b8 Outstanding Requ"..., 38** ld 0x954e0b8 Outstanding Requests:
    ) = 38
    write(2, " * msgid 1,  origid 1, status In"..., 41 * msgid 1,  origid 1, status InProgress
    ) = 41
    write(2, "   outstanding referrals 0, pare"..., 43   outstanding referrals 0, parent count 0
    ) = 43
    write(2, "  ld 0x954e0b8 request count 1 ("..., 45  ld 0x954e0b8 request count 1 (abandoned 0)
    ) = 45
    write(2, "** ld 0x954e0b8 Response Queue:\n", 32** ld 0x954e0b8 Response Queue:
    ) = 32
    write(2, "   Empty\n", 9   Empty
    )               = 9
    write(2, "  ld 0x954e0b8 response count 0\n", 32  ld 0x954e0b8 response count 0
    ) = 32
    write(2, "ldap_chkResponseList ld 0x954e0b"..., 48ldap_chkResponseList ld 0x954e0b8 msgid 1 all 1
    ) = 48
    write(2, "ldap_chkResponseList returns ld "..., 47ldap_chkResponseList returns ld 0x954e0b8 NULL
    ) = 47
    write(2, "ldap_int_select\n", 16ldap_int_select
    )       = 16
    poll([{fd=3, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, -1) = 1 ([{fd=3, revents=POLLIN}])
    write(2, "read1msg: ld 0x954e0b8 msgid 1 a"..., 37read1msg: ld 0x954e0b8 msgid 1 all 1
    ) = 37
    read(3, "", 8)                          = 0
    write(2, "ldap_err2string\n", 16ldap_err2string
    )       = 16
    write(2, "PHP Warning:  ldap_bind(): Unabl"..., 158PHP Warning:  ldap_bind(): Unable to bind to server: Can't contact LDAP server in

И у меня есть исправление /etc/ldap.conf с 'TLS_REQCERT never' - даже если это исправление для другой ошибки, которая дает довольно четкое сообщение об ошибке.

Ответы [ 5 ]

4 голосов
/ 14 марта 2011

Вы видели комментарий на странице PHP.net об отсутствующих разрешениях в каком-либо хранилище сертификатов, которое делает это:

http://de3.php.net/manual/en/function.ldap-connect.php

блеф 27 февраля 2008 10:30 Все пишут о том, как получить ldaps: // работая в стеке WAMP / AD, мне было нелегко найти способ запустить его в RHEL 5.1 (со всеми стандартными оборотами). Старый добрый strace сделал свое дело и помог мне найти проблему ... Оказалось, что php искал файл CA в / etc / pki / CA, и у меня не было правильных разрешений для папки. chmod'ing 755 решил мое сообщение «Не удается связаться с сервером LDAP».

Так что, возможно, это и твоя проблема. Если нет, вы должны попробовать либо strace, либо wireshark перехватить системные вызовы и сетевые передачи и выяснить, что идет не так. Один из двух покажет это ясно.

3 голосов
/ 14 марта 2011

Вот как я это делаю:

<code><?php
    $username = ''; // username to check
    $password = ''; // password to check

/**
 * Is it an Active Directory?
 *
 * <pre>
 * true = yes
 *        set the following values:
 *        SDB_AUTH_LDAP_HOST
 *        SDB_AUTH_LDAP_SSL
 *        SDB_AUTH_LDAP_BASE
 *        SDB_AUTH_LDAP_SEARCH
 *        SDB_AUTH_LDAP_USERDOMAIN
 * false = no, you have to supply an hostname
 *         and configure the following values:
 *         SDB_AUTH_LDAP_HOST
 *         SDB_AUTH_LDAP_PORT
 *         SDB_AUTH_LDAP_SSL
 *         SDB_AUTH_LDAP_BASE
 *         SDB_AUTH_LDAP_SEARCH
 *         SDB_AUTH_LDAP_USERDOMAIN
 * 
* @see SDB_AUTH_LDAP_HOST * / define ('SDB_AUTH_IS_AD', true);/ ** * доменное имя хоста LDAP или домена AD * / define ('SDB_AUTH_LDAP_HOST', 'your-domain.tld');/ ** * Порт LDAP?* * если {@link SDB_AUTH_IS_AD} = true, то порт будет считываться из DNS.* / define ('SDB_AUTH_LDAP_PORT', '389');/ ** * Использовать LDAPS (true) или LDAP (false) соединение?* / define ('SDB_AUTH_LDAP_SSL', false);/ ** * База LDAP * / define ('SDB_AUTH_LDAP_BASE', 'CN = Пользователи, DC = your-domain.tld, DC = de');/ ** * Поиск LDAP, чтобы найти пользователя * *% s будет заменен именем пользователя.
* zB CN =% s * / define ('SDB_AUTH_LDAP_SEARCH', '(& (sAMAccountName =% s) (объектный класс= пользователь) (ObjectCategory = человек)) ');/ ** * Die LDAP Domain des Benutzers * * если имя пользователя не содержит домен, добавьте этот домен к нему.
* если это поле пусто, ничего не будет добавлено.* / define ('SDB_AUTH_LDAP_USERDOMAIN', 'your-domain.tld');/ ** * Путь к поиску LDAP * * вернет лучшие сообщения об ошибках * (оставьте пустым, если вы не хотите его иметь.) * / Define ('SDB_AUTH_LDAP_SEARCHBIN', '/ usr / bin / ldapsearch');$ ldap_error_codes = array ('525' => 'Имя пользователя не существует.', '52e' => 'Неверный пароль.', '530' => 'Вы не можете войти в систему в это время.', '531' => 'Youне удается войти с этого хоста. ',' 532 '=>' Ваш пароль истек. ',' 533 '=>' Ваш аккаунт деактивирован. ',' 701 '=>' Ваш аккаунт истек. ',' 773'=>' Пожалуйста, установите другой пароль (на вашей рабочей станции) перед входом в систему. ',' 775 '=>' Ваш аккаунт заблокирован. ',);if (SDB_AUTH_LDAP_SSL) $ dcs = dns_get_record ("_ ldaps._tcp.". SDB_AUTH_LDAP_HOST, DNS_SRV);иначе $ dcs = dns_get_record ("_ ldap._tcp.". SDB_AUTH_LDAP_HOST, DNS_SRV);перетасовать ($ РС);$ _LDAP_ATTRS = array ('cn', 'sn', 'description', 'GivenName', 'ProperivName', 'displayName', 'memberOf', 'name', 'sAMAccountName', 'sAMAccountType', 'objectClass', 'ObjectCategory ');if (SDB_AUTH_LDAP_USERDOMAIN! = '' && strstr ($ username, '@') === false) {$username=$username.'@'.SDB_AUTH_LDAP_USERDOMAIN;} $ status = array ();$ Статус [ 'CN'] = '';$ Статус [ 'DISPLAYNAME'] = '';$ Статус [ 'описание'] = '';$ Статус [ 'DistinguishedName'] = '';$ Статус [ 'группы'] = массив ();статус [ 'RC'] = массив $ ();$ Статус [ 'подключен'] = ложь;$ Статус [ 'user_exists'] = ложь;$ Статус [ 'is_in_team'] = ложь;foreach ($ dcs as $ _LDAP_HOST) {$ _LDAP_PORT = $ _ LDAP_HOST ['port'];$ _LDAP_HOST = $ _ LDAP_HOST [ 'цель'];// сначала проверяем соединение (http://bugs.php.net/bug.php?id=15637) $ sock = @ fsockopen ($ _ LDAP_HOST, $ _LDAP_PORT, $ errno, $ errstr, 1);@fclose ($ носок);if ($ errno! = 0) продолжить;// затем выполнить "соединение" ... (реальное соединение происходит с помощью bind) $ ds = @ ldap_connect ((SDB_AUTH_LDAP_SSL? "ldaps: //": "ldap: //"). $ _ LDAP_HOST. ":".$ _LDAP_PORT "/").ldap_set_option ($ ds, LDAP_OPT_PROTOCOL_VERSION, 3);// мы связаны?на самом деле, это всегда будет возвращать true, если (is_resource ($ ds)) {$ status ['connected'] = true;// логин успешен?на самом деле также проверка соединения if (@ldap_bind ($ ds, $ username, $ password)) {// search $ sr = ldap_search ($ ds, SDB_AUTH_LDAP_BASE, sprintf (SDB_AUTH_LDAP_SEARCH, $ usernode), $ _LDAP_ATTRS);// такой успешный?if (is_resource ($ sr)) {// получить записи $ info = ldap_get_entries ($ ds, $ sr);if (isset ($ info ['count']) && $ info ['count']> 0) {$ status ['user_exists'] = true;} // закрываем результат поиска ldap_free_result ($ sr);$ Статус [ 'CN'] = $ информация [0] [ 'сп'] [0];$ Статус [ 'описание'] = $ информация [0] [ 'описание'] [0];$ Статус [ 'DISPLAYNAME'] = $ информация [0] [ 'DisplayName'] [0];$ Статус [ 'DistinguishedName'] = $ информация [0] [ 'DistinguishedName'] [0];// пользователь в dexteam?для ($ i = 0; $ i <$ info [0] ['memberof'] ['count']; $ i ++) {$ status ['groups'] [] = $ info [0] ['memberof'][$ я];// В ПРОВЕРКЕ КОМАНДЫ if (substr ($ info [0] ['memberof'] [$ i], 0, strlen ('CN = DexTeam,')) == 'CN = DexTeam,') $ status ['is_in_team'] = true; } $ Статус [ 'RC'] [ 'код'] = ldap_errno ($ Д.С.); $ Статус [ 'RC'] [ 'строка'] = ldap_error ($ Д.С.); ldap_close ($ Д.С.); перерыв; } еще { $ Статус [ 'RC'] [ 'код'] = ldap_errno ($ Д.С.); $ Статус [ 'RC'] [ 'строка'] = ldap_error ($ Д.С.); ldap_close ($ Д.С.); перерыв; } } еще { $ Статус [ 'RC'] [ 'код'] = ldap_errno ($ Д.С.); $ Статус [ 'RC'] [ 'строка'] = ldap_error ($ Д.С.); // мы хотим лучше сообщения об ошибках? if (SDB_AUTH_LDAP_SEARCHBIN! = '' && is_executable (SDB_AUTH_LDAP_SEARCHBIN)) { $ Статус [ 'RC'] [ 'ldapsearchrc'] = ''; $ Статус [ 'RC'] [ 'ldapsearchtxt'] = массив (); exec (SDB_AUTH_LDAP_SEARCHBIN. '-x -H' .escapeshellarg ((SDB_AUTH_LDAP_SSL? "ldaps: //": "ldap: //"). $ _ LDAP_HOST. ":". $ _ LDAP_PORT. "/"). '-D'). .escapeshellarg ($ username). '-w' .escapeshellarg ($ password). '2> & 1', $ status ['RC'] ['ldapsearchtxt'], $ status ['RC'] ['ldapsearchrc']) ; if ($ status ['RC'] ['ldapsearchrc']! = 0) { if (preg_match ("/ data ([^,] +), /", $ status ['RC'] ['ldapsearchtxt'] [1], $ соответствует)) { if (isset ($ ldap_error_codes [$ соответствия [1]])) { $ Статус [ 'RC'] [ 'код'] = $ соответствует [1]; $ Статус [ 'RC'] [ 'строка'] = $ ldap_error_codes [$ матчей [1]]; } } снята с охраны ($ статус [ 'RC'] [ 'ldapsearchrc']); снята с охраны ($ статус [ 'RC'] [ 'ldapsearchtxt']); } } ldap_close ($ Д.С.); перерыв; } } еще { Продолжить; } }

вы включили сертификат? я знаю, что была проблема, когда сертификат отказался. отредактируйте "/etc/ldap/ldap.conf" и добавьте "TLS_REQCERT never"

#
# LDAP Defaults
#
# See ldap.conf(5) for details
# This file should be world readable but not world writable.
#BASE   dc=example,dc=com
#URI    ldap://ldap.example.com ldap://ldap-master.example.com:666
#SIZELIMIT      12
#TIMELIMIT      15
#DEREF          never
TLS_REQCERT never

однако, для меня это работает с ldap и ldaps:

  • это может быть проблема с настройкой рекламы. возможно, снизит некоторые ограничения безопасности ...
  • ИЛИ это может быть также проблема с php / ldap lib. Попробуйте обновить до новых версий:)
1 голос
/ 03 июня 2011

Поскольку мой код отлично работает с CentOS, я пришел к выводу, что проблема не в программировании.Я пока не смог запустить его в своей среде Ubuntu, но я предполагаю, что это ошибка в моем серверном программном обеспечении.

1 голос
/ 28 марта 2011

Я наконец-то смог заставить все работать на моей машине с Windows, прочитав следующую ветку ошибок PHP: http://bugs.php.net/bug.php?id=48866

К сожалению, это специфично для Windows, но это заставило меня, по крайней мере, сейчас двигаться в правильном направлениив моем тестировании (я знаю, что теперь он должен работать из PHP на моем веб-сервере ... при условии, что ldap.conf настроен правильно).В Windows с PHP 5.3 мне нужно было добавить файл ldap.conf в корень моего диска C: (другие примеры, которые я видел в Интернете, помещали его в C: \ openldap \ sysconf, который не работал).

TLS все еще не работает точно (это дает мне сообщение «невозможно запустить tls: не удается связаться с сервером LDAP»), но SSL, похоже, работает, и я смог обновить пароль для учетной записи вмой тестовый сценарий.

Я предполагаю, что файл ldap.conf просто необходимо настроить аналогичным образом на моем веб-сервере, и я надеюсь, что буду в бизнесе (я просто не уверен, что тот, который уже существуеттот, который мне нужно изменить, или если мне нужно создать дополнительный).Я посмотрю, смогу ли я доложить об этом.

0 голосов
/ 10 марта 2011

В вашем Active Directory включен LDAPS?Если это так, получите доверенный корень ключа CA в хранилище ключей доверенного корня.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...