多Windows域LDAP用户认证异常问题求助
多Windows域LDAP认证证书冲突问题
问题描述
我需要通过LDAP对3个独立Windows域的用户进行认证,每个域有专属DC,证书直接在PHP代码中配置。目前遇到的问题是:Apache重启后,先登录的域用户可正常完成认证,其他域用户登录时,Apache错误日志会出现证书颁发者验证失败的错误;重启Apache后切换至其他域登录,原域用户又无法认证。
关键代码
$certpath=$this->globalparameter->parameters["constants"]["CertifPath"]; switch ($domainname) { case "imofa": $DC=$this->globalparameter->parameters["constants"]["DcIMF"]; $certname=$this->globalparameter->parameters["constants"]["CertifIMF"]; break; case "havexmobility2": $upn=str_ireplace("mobility2","mobility",$upn); //kvůli úpravě DC pro o365 $DC=$this->globalparameter->parameters["constants"]["DcHXM"]; $certname=$this->globalparameter->parameters["constants"]["CertifHXM"]; break; default: // default je "havex2" $upn=str_ireplace("havex2","havex",$upn); //kvůli úpravě DC pro o365 $DC=$this->globalparameter->parameters["constants"]["DcHXA"]; $certname=$this->globalparameter->parameters["constants"]["CertifHXA"]; } ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7); //zapnout debug ldap_set_option(null, LDAP_OPT_X_TLS_CACERTDIR, $certpath); ldap_set_option(null, LDAP_OPT_X_TLS_CACERTFILE, $certpath.'\\'.$certname); $s="ldaps://".$DC; $ds= ldap_connect($s,636); if ($ds) { ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3); ldap_set_option($ds, LDAP_OPT_REFERRALS, 0); error_reporting(0); ldap_start_tls($ds); $r=ldap_bind ($ds, $upn, $password); // ... 后续代码 ldap_unbind($ds);
日志示例
正常登录日志
ldap_connect_to_host: TCP DCM1.HAVEXMOBILITY2.CZ:636 ldap_new_socket: 1660 ldap_prepare_socket: 1660 ldap_connect_to_host: Trying 192.168.30.250:636 ldap_pvt_connect: fd: 1660 tm: -1 async: 0 attempting to connect: connect success TLS trace: SSL_connect:before SSL initialization TLS trace: SSL_connect:SSLv3/TLS write client hello TLS trace: SSL_connect:SSLv3/TLS write client hello TLS trace: SSL_connect:SSLv3/TLS read server hello TLS certificate verification: depth: 1, err: 0, subject: /DC=cz/DC=havexmobility2/CN=havexmobility2-DCM1-CA, issuer: /DC=cz/DC=havexmobility2/CN=havexmobility2-DCM1-CA TLS certificate verification: depth: 0, err: 0, subject: /CN=DCM1.havexmobility2.cz, issuer: /DC=cz/DC=havexmobility2/CN=havexmobility2-DCM1-CA TLS trace: SSL_connect:SSLv3/TLS read server certificate TLS trace: SSL_connect:SSLv3/TLS read server key exchange TLS trace: SSL_connect:SSLv3/TLS read server certificate request TLS trace: SSL_connect:SSLv3/TLS read server done
认证失败日志
connect success TLS trace: SSL_connect:before SSL initialization TLS trace: SSL_connect:SSLv3/TLS write client hello TLS trace: SSL_connect:SSLv3/TLS write client hello TLS trace: SSL_connect:SSLv3/TLS read server hello TLS certificate verification: depth: 0, err: 20, subject: /CN=DCM1.havexmobility2.cz, issuer: /DC=cz/DC=havexmobility2/CN=havexmobility2-DCM1-CA TLS certificate verification: Error, unable to get local issuer certificate TLS trace: SSL3 alert write:fatal:unknown CA TLS trace: SSL_connect:error in error TLS: can't connect: error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed (unable to get local issuer certificate). ldap_err2string
回答
问题根源在于你使用NULL作为ldap_set_option的第一个参数,这会设置全局LDAP选项。Apache是多进程模型,每个进程的全局LDAP选项会被缓存,第一个请求设置的证书路径会被该进程后续所有请求复用,导致其他域的证书验证失败。
你可以通过以下几种方式解决:
针对单个连接设置TLS选项(推荐)
不要使用全局选项,而是在ldap_connect成功后,针对当前连接$ds设置TLS相关选项:$ds= ldap_connect($s,636); if ($ds) { // 针对当前连接设置TLS选项 ldap_set_option($ds, LDAP_OPT_X_TLS_CACERTDIR, $certpath); ldap_set_option($ds, LDAP_OPT_X_TLS_CACERTFILE, $certpath.'\\'.$certname); ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3); ldap_set_option($ds, LDAP_OPT_REFERRALS, 0); error_reporting(0); ldap_start_tls($ds); $r=ldap_bind ($ds, $upn, $password); // ... 后续代码 }这样每个连接都会使用对应域的证书,不会互相干扰。
合并所有CA证书到单个文件
将三个域的CA证书合并成一个PEM文件,然后全局设置这个合并后的证书文件。这样不管哪个域的DC证书,都能通过这个合并文件完成验证,无需每次切换证书路径。重置全局选项(可靠性较低)
在设置新的证书路径前,先尝试将LDAP_OPT_X_TLS_CACERTFILE设置为空,再设置新路径,但这种方式无法完全规避Apache进程的全局状态缓存问题,不推荐作为主要解决方案。
内容的提问来源于stack exchange,提问作者Petr Domácí
相关产品推荐
相关产品推荐

