web-dev-qa-db-ja.com

PHP)を使用したActiveDirectoryへの安全なバインドに関する問題

Phpを使用してActiveDirectoryに安全にバインドできないようです。暗号化されていない接続は正常に機能します。他のクライアントを使用すると、安全にバインドできます。 SSLを介したLDAPAdminを使用した接続。ここでの問題は何ですか?不足しているLDAPSSLモジュールはありますか? 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 

Ubuntu10.04リポジトリのPHPバージョン5.3.2-1ubuntu4.7)を使用してActiveDirectoryサーバーにバインドしようとしています

$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_REQCERTnever」で修正しています-この修正は別のエラーに対するものですが、かなり明確なエラーメッセージが表示されます。

11
Jon Skarpeteig

私のコードはCentOSで正常に機能しているので、問題はプログラミング固有のものではないと結論付けます。現時点では、Ubuntu環境で実行することはできませんが、これはサーバーソフトウェアのバグだと思います。

0
Jon Skarpeteig

これを行ういくつかの証明書ストアでのアクセス許可の欠落に関するPHP.netページのコメントを見ましたか?

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

bleathem 2008年2月27日10:30誰もがldaps://をWAMP/ADスタックで動作させることについて投稿していますが、RHEL 5.1(すべてのストックrpmを使用)で動作させる方法を見つけるのに苦労しました。古き良きstraceがそのトリックを実行し、問題を見つけるのに役立ちました... phpが/ etc/pki/CAでCAファイルを探していたことが判明し、フォルダーに対する正しいアクセス許可がありませんでした。 755にchmodすると、「LDAPサーバーに接続できません」というメッセージが解決されました。

だから多分それもあなたの問題です。そうでない場合は、straceまたはwiresharkのいずれかを試して、システムコールとネットワーク送信をキャッチし、何が問題になっているのかを把握する必要があります。 2つのうちの1つはそれをはっきりと示します。

4
schlenk

これは私がそれをする方法です:

<?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
 * </pre>
 * @see SDB_AUTH_LDAP_Host
 */
define('SDB_AUTH_IS_AD', true);
/**
 * Domain name of the LDAP Host or of the AD-Domain
 */
define('SDB_AUTH_LDAP_Host', 'your-domain.tld');
/**
 * LDAP Port?
 *
 * if {@link SDB_AUTH_IS_AD} = true, then the port will be read form DNS.
 */
define('SDB_AUTH_LDAP_PORT', '389');
/**
 * Use LDAPS (true) oder LDAP (false) connection?
 */
define('SDB_AUTH_LDAP_SSL', false);
/**
 * LDAP Base
 */
define('SDB_AUTH_LDAP_BASE', 'CN=Users,DC=your-domain.tld,DC=de');
/**
 * LDAP Search, to find a user
 *
 * %s will be replaced by the username.<br>
 * z.B. CN=%s
 */
define('SDB_AUTH_LDAP_SEARCH', '(&(sAMAccountName=%s)(objectclass=user)(objectcategory=person))');
/**
 * Die LDAP Domain des Benutzers
 *
 * if the username doesnt contain a domain append this domain to it.<br>
 * in case this is empty, nothing will be appended.
 */
define('SDB_AUTH_LDAP_USERDOMAIN', 'your-domain.tld');
/**
 * Path to LDAP Search
 *
 * Will give back better error messages
 * ( leave empty in case you don't want to have it. )
 */
define('SDB_AUTH_LDAP_SEARCHBIN', '/usr/bin/ldapsearch');




        $ldap_error_codes=array(
        '525' => 'Username doesnt exist.',
        '52e' => 'Wrong password.',
        '530' => 'You cannot login at this time.',
        '531' => 'You cannot login from this Host.',
        '532' => 'Your password was expired.',
        '533' => 'Your account has been deactivated.',
        '701' => 'Your account was expired.',
        '773' => 'Please set another password (at your workstation) before you login.',
        '775' => 'Your account has been locked.',
        );


  if(SDB_AUTH_LDAP_SSL) $dcs=dns_get_record("_ldaps._tcp.".SDB_AUTH_LDAP_Host, DNS_SRV); else $dcs=dns_get_record("_ldap._tcp.".SDB_AUTH_LDAP_Host, DNS_SRV);
  shuffle($dcs);

  $_LDAP_ATTRS=array('cn', 'sn', 'description', 'givenName', 'distinguishedName', 'displayName', 'memberOf', 'name', 'sAMAccountName', 'sAMAccountType', 'objectClass', 'objectCategory');
  if(SDB_AUTH_LDAP_USERDOMAIN!='' && strstr($username, '@')===false) {
        $username=$username.'@'.SDB_AUTH_LDAP_USERDOMAIN;
  }
  $status=array();
  $status['CN']='';
  $status['displayName']='';
  $status['description']='';
  $status['distinguishedName']='';
  $status['groups']=array();
  $status['RC']=array();
  $status['connected']=false;
  $status['user_exists']=false;
  $status['is_in_team']=false;

foreach($dcs as $_LDAP_Host) {
$_LDAP_PORT=$_LDAP_Host['port'];
$_LDAP_Host=$_LDAP_Host['target'];
// check connection first ( http://bugs.php.net/bug.php?id=15637 )
$sock=@fsockopen($_LDAP_Host, $_LDAP_PORT, $errno, $errstr, 1);
@fclose($sock);
if($errno!=0) continue;

// then do a "connect"... ( the real connect happens with bind )
$ds=@ldap_connect(( SDB_AUTH_LDAP_SSL ? "ldaps://" : "ldap://" ).$_LDAP_Host.":".$_LDAP_PORT."/");
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
// are we connected? actually, this will always return true
if(is_resource($ds)) {
    $status['connected']=true;
    // login sucessful? actually also connection test
    if(@ldap_bind($ds, $username, $password)) {
        // search
        $sr=ldap_search($ds, SDB_AUTH_LDAP_BASE, sprintf(SDB_AUTH_LDAP_SEARCH, $usernode), $_LDAP_ATTRS);
        // suche successful?
        if(is_resource($sr)) {

            // fetch entries
            $info = ldap_get_entries($ds, $sr);
            if(isset($info['count']) && $info['count']>0) {
                $status['user_exists']=true;
            }
            // close search result
            ldap_free_result($sr);
            $status['CN']=$info[0]['cn'][0];
            $status['description']=$info[0]['description'][0];
            $status['displayName']=$info[0]['displayname'][0];
            $status['distinguishedName']=$info[0]['distinguishedname'][0];
            // is the user in the dexteam?
            for($i=0; $i<$info[0]['memberof']['count']; $i++) {
                $status['groups'][]=$info[0]['memberof'][$i];
                // IS IN TEAM CHECK 
                if(substr($info[0]['memberof'][$i], 0, strlen('CN=DexTeam,'))=='CN=DexTeam,') $status['is_in_team']=true; 
            }

            $status['RC']['code']=ldap_errno($ds);
            $status['RC']['string']=ldap_error($ds);
            ldap_close($ds);
            break;
        }
        else {
            $status['RC']['code']=ldap_errno($ds);
            $status['RC']['string']=ldap_error($ds);
            ldap_close($ds);
            break;
        }
    }
    else {
        $status['RC']['code']=ldap_errno($ds);
        $status['RC']['string']=ldap_error($ds);
        // do we want better error messages?
        if(SDB_AUTH_LDAP_SEARCHBIN!='' && is_executable(SDB_AUTH_LDAP_SEARCHBIN)) {
            $status['RC']['ldapsearchrc']='';
            $status['RC']['ldapsearchtxt']=array();
            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], $matches)) {
                    if(isset($ldap_error_codes[$matches[1]])) {
                        $status['RC']['code']=$matches[1];
                        $status['RC']['string']=$ldap_error_codes[$matches[1]];
                    }
                }
                unset($status['RC']['ldapsearchrc']);
                unset($status['RC']['ldapsearchtxt']);
            }
        }
        ldap_close($ds);
        break;
    }
}
else {
    continue;
}
}

証明書を有効にしましたか?証明書が拒否されたときに問題があったことを私は知っています。 「/etc/ldap/ldap.conf」を編集し、「TLS_REQCERTnever」を追加します

#
# 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/ldaplibの問題である可能性もあります。新しいバージョンに更新してみてください:)
3
JMW

次のPHPバグスレッド: http://bugs.php.net/bug.php?id=48866)を読むことで、ようやくWindowsマシンで動作させることができました。

残念ながら、これはWindows固有ですが、テストでは少なくとも正しい方向に進むことができました(Webサーバー上でPHPから動作するはずです... ldap.confが正しく構成されている)PHP 5.3のWindowsでは、ldap.confファイルをC:ドライブのルートに追加する必要がありました(オンラインで見た他の例では、それを配置していました)。動作していなかったC:\ openldap\sysconf内)。

TLSはまだ正確には機能しません(「tlsを開始できません:LDAPサーバーに接続できません」というメッセージが表示されます)が、SSLは機能しているように見え、テストスクリプトでアカウントのパスワードを更新できました。

Ldap.confファイルをWebサーバーで同様に設定する必要があると思いますが、うまくいけばビジネスを行う必要があります(既に存在するファイルが変更する必要があるのか​​、それとも必要なのかはわかりません。追加のものを作成します)。その前に報告できるかどうかを確認します。

1
Omar Ramos

Active DirectoryでLDAPSが有効になっていますか?その場合は、CAのキーの信頼されたルートを信頼されたルートキーストアに取得します。

0
geoffc

WebとSO全体からソリューションを読んで試した後、私の一日を救ったのは、ポートなしでldaps uriを使用する指定することでした。

したがって、これの代わりに:ldaps://example.com:636私はこれを使わなければなりませんでした:ldaps://example.comそしてそれは今では魅力のように機能します。

私はこれをUbuntu16.04でセットアップし、PHP7.3をNginxとphp-fpmで実行していました。

完全なコード例:

try{
    $ldapUri = "ldaps://example.com";
    $ldapUsername = 'username';
    $ldapPassword = 'password';
    $ldapConn = ldap_connect($ldapUri);
    if($ldapConn){
        ldap_set_option($ldapConn,LDAP_OPT_NETWORK_TIMEOUT,10);

        if(!ldap_set_option($ldapConn,LDAP_OPT_PROTOCOL_VERSION,3)){
           print 'Failed to set ldap protocol to version 3<br>';
        }
        ldap_set_option($ldapConn, LDAP_OPT_REFERRALS,0);
        $ldapBind = ldap_bind($ldapConn, $ldapUsername, $ldapPass);
        if ($ldapBind) {
           echo "LDAP bind successful...";
           //DO LDAP search and stuff
           ldap_unbind($ldapConn);
        } else {
           echo "LDAP bind failed...";
        }
    }
}catch(Exception $e){
    print($e->getMessage();
}
0
ak93