gpt4 book ai didi

php - 当密码包含“&”号时,php ldap_bind失败

转载 作者:可可西里 更新时间:2023-11-01 00:34:23 25 4
gpt4 key购买 nike

我的active directory上有一个用于身份验证的函数,它工作正常,除了一些包含特殊字符的密码,例如:
< >
总是返回“无效凭据”
我在网上看过好几篇文章,但似乎没有一篇是正确的。
我使用的是php version 5.3.8-zs(zend服务器)
以下是我的类构造函数中的设置:

ldap_set_option($this->_link, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($this->_link, LDAP_OPT_REFERRALS, 0);

以及登录功能:
public function login($userlogin,$userpassword){
$return=array();
$userlogin.="@".$this->_config["domaine"];
$ret=@ldap_bind($this->_link , $userlogin ,utf8_decode($userpassword));
if(!$ret){
$return[]=array("status"=>"error","message"=>"Erreur d'authentification ! <br/> Veuillez vérifier votre nom et mot de passe SVP","ldap_error"=>ldap_error($this->_link));
}
if($ret)
{
$return[]=array("status"=>"success","message"=>"Authentification réussie");
}
return json_encode($return);
}

任何帮助都将不胜感激。

最佳答案

我们发现自己处于一个类似的环境中,我们的企业环境ldap身份验证设置对于我们的一个内部托管web应用程序似乎工作得很好,直到用户的密码中有特殊字符,比如?*想使用该应用程序,然后很明显,有些东西实际上没有按预期工作。
当受影响的用户密码中出现特殊字符时,我们发现我们的身份验证请求失败到ldap_bind(),并且常见的错误是如Invalid credentialsNDS error: failed authentication (-669)(从LDAP的扩展错误日志记录)输出到日志。事实上,是NDS error: failed authentication (-669)错误导致了失败的绑定很可能是由于密码中存在特殊字符——通过Novell支持文章[AA],其中最显著的部分摘录如下:
管理密码包含LDAP无法理解的字符,即:“\
和“$”
已经对一系列修复进行了广泛的测试,包括通过html_entity_decode()utf8_encode()等来“清除”密码,并确保web应用程序和各种服务器之间没有密码的错误编码,但是无论做什么来调整或保护输入文本,以确保其原始的UTF-8编码保持完整,ldap_bind()当密码中存在一个或多个特殊字符时,总是失败(我们没有用用户名中的特殊字符测试用例)。但也有人报告说,当用户名包含特殊字符而不是密码或密码之外的字符时,也会出现类似的问题。
我们还使用在命令行上运行的最小php脚本直接测试了ldap_bind(),该脚本使用硬编码用户名和密码对密码中包含特殊字符(包括问号、星号和感叹号)的测试ldap帐户进行了测试,试图尽可能多地消除失败的潜在原因,但是绑定操作仍然失败。因此,很明显,这不是密码在Web应用程序和服务器之间错误编码的问题,所有这些设计和构建都是符合UTF-8的端到端的。相反,它似乎有一个潜在的问题与CC及其特殊字符的处理。同一个测试脚本成功地绑定了另一个密码中没有任何特殊字符的帐户,进一步证实了我们的怀疑。
早期版本的ldap身份验证的工作方式如下,这是大多数php ldap身份验证教程建议的:

$success = false; // could we authenticate the user?

if(!defined("LDAP_OPT_DIAGNOSTIC_MESSAGE")) {
define("LDAP_OPT_DIAGNOSTIC_MESSAGE", 0x0032); // needed for more detailed logging
}

if(($ldap = ldap_connect($ldap_server)) !== false && is_resource($ldap)) {
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);

if(@ldap_bind($ldap, sprintf("cn=%s,ou=Workers,o=Internal", $username), $password)) {
$success = true; // login succeeded...
} else {
error_log(sprintf("* Unable to authenticate user (%s) against LDAP!", $username));
error_log(sprintf(" * LDAP Error: %s", ldap_error($ldap)));

if(ldap_get_option($ldap, LDAP_OPT_DIAGNOSTIC_MESSAGE, $extended_error)) {
error_log(sprintf(" * LDAP Extended Error: %s\n", $extended_error));
unset($extended_error);
}
}
}
@ldap_close($ldap); unset($ldap);

很明显,我们使用的仅仅是 ldap_bind()ldap_connect()的简单解决方案将不允许用户使用复杂的密码进行身份验证,我们必须找到一个替代方案。
我们选择了基于使用系统帐户(创建时具有搜索用户所需的权限,但有意配置为有限的只读帐户)第一次绑定到LDAP服务器的解决方案。然后,我们使用 ldap_bind()搜索具有所提供用户名的用户帐户,然后使用 ldap_search()将用户提供的密码与ldap中存储的密码进行比较。这个解决方案已经被证明是可靠和有效的,我们现在能够验证用户的特殊字符在他们的密码和那些没有!
新的LDAP进程的工作方式如下:
if(!defined("LDAP_OPT_DIAGNOSTIC_MESSAGE")) {
define("LDAP_OPT_DIAGNOSTIC_MESSAGE", 0x0032); // needed for more detailed logging
}

$success = false; // could we authenticate the user?

if(($ldap = @ldap_connect($ldap_server)) !== false && is_resource($ldap)) {
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);

// instead of trying to bind the user, we bind to the server...
if(@ldap_bind($ldap, $ldap_username, $ldap_password)) {
// then we search for the given user...
if(($search = ldap_search($ldap, "ou=Workers,o=Internal", sprintf("(&(uid=%s))", $username))) !== false && is_resource($search)) {
// they should be the first and only user found for the search...
if((ldap_count_entries($ldap, $search) == 1) && ($entry = ldap_first_entry($ldap, $search)) !== false && is_resource($entry)) {
// we ensure this is the case by obtaining the user identifier (UID) from the search which must match the provided $username...
if(($uid = ldap_get_values($ldap, $entry, "uid")) !== false && is_array($uid)) {
// ensure that just one entry was returned by ldap_get_values() and ensure the obtained value matches the provided $username (excluding any case-differences)
if((isset($uid["count"]) && $uid["count"] == 1) && (isset($uid[0]) && is_string($uid[0]) && (strcmp(mb_strtolower($uid[0], "UTF-8"), mb_strtolower($username, "UTF-8")) === 0))) {
// once we have compared the provied $username with the discovered username, we get the DN for the user, which we need to provide to ldap_compare()...
if(($dn = ldap_get_dn($ldap, $entry)) !== false && is_string($dn)) {
// we then use ldap_compare() to compare the user's password with the provided $password
// ldap_compare() will respond with a boolean true/false depending on if the comparison succeeded or not, and -1 on error...
if(($comparison = ldap_compare($ldap, $dn, "userPassword", $password)) !== -1 && is_bool($comparison)) {
if($comparison === true) {
$success = true; // user successfully authenticated, if we don't get this far, it failed...
}
}
}
}
}
}
}
}

if(!$success) { // if not successful, either an error occurred or the credentials were actually incorrect
error_log(sprintf("* Unable to authenticate user (%s) against LDAP!", $username));
error_log(sprintf(" * LDAP Error: %s", ldap_error($ldap)));
if(ldap_get_option($ldap, LDAP_OPT_DIAGNOSTIC_MESSAGE, $extended_error)) {
error_log(sprintf(" * LDAP Extended Error: %s\n", $extended_error));
unset($extended_error);
}
}
}
@ldap_close($ldap);
unset($ldap);

一个最初的担忧是,这种新的方法,所有额外的步骤将需要更长的时间来执行,但事实证明(至少在我们的环境中),运行一个简单的 ldap_compare()和使用 ldap_bind()ldap_search()的更复杂的解决方案之间的时间差实际上是微不足道的,平均可能要长0.1s。
请注意,只有在验证和测试用户输入数据(即用户名和密码输入)以确保没有提供空的或无效的用户名或密码字符串之后,才能运行早期和后期版本的LDAP验证代码。并且不存在无效字符(如空字节)。此外,用户证书应该理想地通过来自应用的安全HTTPS请求来发送,并且安全LDAP协议可以用于在认证过程中进一步保护用户凭证。我们发现直接使用ldap,通过 ldap_compare()连接到服务器,而不是通过 ldaps://...连接,然后切换到tls连接被证明是更可靠的。如果您的设置有所不同,则需要相应地调整上面的代码,以包含对 ldap://...的支持。
最后,值得注意的是,ldap协议要求根据rfc( https://www.novell.com/support/kb/doc.php?id=3335671)使用utf-8对密码进行编码,因此,如果在请求期间使用的任何系统从初始输入一直到身份验证都没有将用户的凭据作为utf-8进行处理,这也可能是一个值得调查的领域。
希望这个解决方案将证明对其他人是有用的,他们已经努力解决这个问题与许多其他报告的解决方案,但仍然发现用户无法验证的情况。可能需要针对您自己的LDAP环境调整代码,特别是 ldap_start_tls()中使用的DN,因为这将取决于LDAP目录的配置方式以及您支持应用程序的用户组。很高兴,它在我们的环境中运行得非常好,希望这个解决方案在其他地方也能被证明是有用的。

关于php - 当密码包含“&”号时,php ldap_bind失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12858493/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com