【问题标题】:Changing Active Directory user password with PHP script使用 PHP 脚本更改 Active Directory 用户密码
【发布时间】:2016-03-12 04:07:17
【问题描述】:

我正在尝试获取一个非常简单的 PHP 脚本来更改我的 Active Directory 域中的用户 密码

这是我在网上找到的一些脚本:

<?php
$uid = 'Mohammed Noureldin';
$newPassword = '5omeGoodP@ssword';
$bindDn = 'CN=Administrator,OU=UsersOU,DC=example,DC=local';
$bindPassword = 'An0therGoodP@ssword';
$baseDn = 'OU=UsersOU,DC=example,DC=local';
$protocolVersion = 3;

$ldap = ldap_connect('localhost');
if (!ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, $protocolVersion))
{
    exit('Failed to set protocol version to '.$protocolVersion);
}
// bind anonymously so that we can verify if the server really is running
ldap_bind($ldap);
if (ldap_errno($ldap) !== 0)
{
    exit('Could not connect to LDAP server');
}

// now bind with the correct username and password
ldap_bind($ldap, $bindDn, $bindPassword);
if (ldap_errno($ldap) !== 0)
{
    exit('ERROR: '.ldap_error($ldap));
}

$searchResults = ldap_search($ldap, $baseDn, 'cn='.$uid);
// no matching records
if ($searchResults === false)
{
    exit('No user found');
}

if (!is_resource($searchResults))
{
    exit('Error in search results.');
}
// create the unicode password
$len = strlen($newPassword);
$newPass = '"';
for ($i = 0; $i < $len; $i++)
{
    $newPass .= "{$newPassword{$i}}\000";
}
$newPass .= '"';

$entry = ldap_first_entry($ldap, $searchResults);
if (!is_resource($entry))
{
    exit('Couldn\'t get entry');
}
$userDn = ldap_get_dn($ldap, $entry);
if (!$userDn)
{
exit('Errrrrrrrrr1');

}
if (!ldap_modify($ldap, $userDn, array('unicodePwd' => $newPass)))
{
exit(ldap_errno($ldap)." ". ldap_error($ldap));

}
?>

这个 PHP 页面的输出是这个 error 消息:

53 服务器不愿意执行

脚本根本不起作用(用户的密码更改)。

我知道 AD 将密码存储在 unicodePwd 字段中的主要原理(如果到目前为止仍然如此),并且我知道我必须使用 安全连接并且我正在使用它(希望它设置正确)。

我在谷歌上搜索了该错误消息,但找不到任何实用的解决方案。

我还尝试了一些其他脚本,但这个是迄今为止最好的,因为其他脚本在之前的一些步骤中给了我一些错误(例如绑定)。

非常感谢解决该问题的任何帮助,甚至另一个功能性脚本可能是个好主意! 提前致谢。

【问题讨论】:

  • 此代码没有安全地连接到 LDAP 服务器。见documentation
  • 如果我在第三方应用程序(Apache Directory Studio)上使用端口 636 和 ldaps,并且我能够连接,这是否意味着我正确使用了安全连接?
  • 这仅表示您的第三方应用程序正在正确使用安全连接。不代表你写的代码就是这样做的!
  • 好的,你能帮我让我的代码使用安全连接吗?我错过了什么?

标签: active-directory php ldap


【解决方案1】:

除非您通过 SSL/TLS 连接,否则您不得使用此方法更改密码。如果您在 Google 或 Bing 中搜索到 unicodePwd 这个词,因为您将它包含在您的帖子中,所以第一个如果不是 第一个结果将是 MSDN documentation for unicodePwd,它在前三句话:

这个属性是由一个LDAP Modify写在下面的 限制条件。 Windows 2000 操作系统服务器需要 客户端具有 128 位(或更好)的 SSL/TLS 加密 连接到 DC 以修改此属性。在 Windows 上 Server 2003 操作系统、Windows Server 2008 操作系统、 Windows Server 2008 R2 操作系统,Windows Server 2012 操作系统 系统、Windows Server 2012 R2 操作系统和 Windows Server 2016 Technical Preview 操作系统,DC 也允许 修改受保护的连接上的 unicodePwd 属性 128 位(或更好)简单身份验证和安全层 (SASL) 层加密而不是 SSL/TLS。在 Windows Server 2008 中, Windows Server 2008 R2、Windows Server 2012、Windows Server 2012 R2、 和 Windows Server 2016 技术预览版,如果 fAllowPasswordOperationsOverNonSecureConnection 启发式的 dSHeuristics 属性(第 6.1.1.2.4.1.2 节)为真且有效 目录作为 AD LDS 运行,则 DC 允许修改 unicodePwd 属性通过一个既不是 SSL/TLS 加密或 SASL 加密。 unicodePwd 属性是 从未被 LDAP 搜索返回。

如果您只是对unicodePwd 执行简单搜索,那么再次搜索第一个 您将获得STEP BY STEP CODE关于如何执行此操作的结果:

https://support.microsoft.com/en-us/kb/269190

【讨论】:

  • 我已经在使用 SSL(希望它设置正确),我编辑了我的问题。无论如何,是否有另一种方法可以在不使用 SSL 的情况下更改密码?
  • 根据您发布的代码,我不相信您正在使用 SSL。没有ldaps://,没有:636,没有LDAPStatus = ldap_get_option(LDAPConnection, LDAP_OPT_SSL, (void *)&amp;SSLOption);
【解决方案2】:

意识到我迟到了一年参加这个聚会;但是在解决类似问题时发现了这篇文章...

怀疑 ldaps 正在/正在提供一个不受托管此 php 脚本的服务器信任的证书(linux?); OP提到已更改代码以使用ldaps并退出('无法连接到LDAP服务器');但通过 Apache Directory Studio 连接正常,它可能在信任证书的机器上(即信任 DC 的加入域的工作站)。

要正确解决此问题,请调查您的私钥基础架构(一个大话题,从这里开始:https://social.technet.microsoft.com/wiki/contents/articles/2980.ldap-over-ssl-ldaps-certificate.aspx

或者,只需告诉 php 服务器信任 LDAP 自签名证书,请参见:Authenticating a self-signed certificate for LDAPS connection

要在开始之前验证这种情况(即不建议在生产中使用),请考虑在 php 主机(假设为 linux 服务器)上配置 LDAP,以通过 put 忽略由证书颁发机构/信任引起的错误

TLS_REQCERT never

在 /etc/ldap/ldap.conf 的末尾 (更改后重启 apache / webserver)

【讨论】:

    【解决方案3】:

    正如 Ryan Ries 所指出的,您必须建立安全连接才能更改密码,但您发布的代码并没有这样做。

    有问题的代码是:

    $ldap = ldap_connect('localhost');
    

    如您所见,这会产生非安全连接。

    To make a secure connection,你需要指定一个LDAPS URI:

    $ldap = ldap_connect('ldaps://localhost');
    

    【讨论】:

    • 当我尝试你的建议时,我得到了这个错误:无法连接到 LDAP 服务器
    • 尝试使用正确的域控制器地址。它可能没有在 localhost 上运行。
    【解决方案4】:

    您创建的 unicode 密码错误。这是至少在 Server 2012 上对我有用的代码。

    // create the unicode password
    $newpassword = "\"" . $newpassword . "\"";
    $len = strlen($newpassword);
    for ($i = 0; $i < $len; $i++) $newpass .= "{$newpassword{$i}}\000";
    $entry["unicodePwd"] = $newpass;
    
    // Modify the password
    if (ldap_mod_replace($ldap, $userDn, $entry))
    {
      exit("Successfully updated password!");
    }
    

    注意我是如何用新密码对 " 进行编码的,这就是让它对我有用的技巧。

    【讨论】:

    • 这不是必需的,因为他将他的报价放在单个刻度中。 '"' 与 "\"" 几乎相同
    【解决方案5】:
    $server = "ldaps://172.1.200.1:636";
    $dn = "dc=srv,dc=world";
    $message = array();
    
    function changePassword($server,$dn,$user,$oldPassword,$newPassword,$newPasswordCnf){
      global $message;
    
      error_reporting(0);
      putenv('LDAPTLS_REQCERT=allow');
      $con=ldap_connect($server);
      ldap_set_option($con, LDAP_OPT_PROTOCOL_VERSION, 3);
      ldap_set_option($con, LDAP_OPT_REFERRALS, 0);
    
      $findWhat = array ("cn","mail");
      $findWhat = array();
      $findWhere = $dn;
      $findFilter = "(sAMAccountName=$user)";
    
      $ldaprdn = 'mydomain' . "\\" . $user;
      $ldapbind = ldap_bind($con, $ldaprdn, $oldPassword);
      if ($ldapbind) {
          $message[] = "ldapbind  ($ldapbind) with sAMAccountName=$user";
      } else {
        $error = ldap_error($con);
        $errno = ldap_errno($con);
        $message[] = "ldapbind error $errno - $error";
        ldap_close($con);
        return false;
      }
      $sr = ldap_search($con,$dn,$findFilter,$findWhat);
      $records = ldap_get_entries($con, $sr);
    
     if ($records["count"] != "1") {
        $message[] = "Error E100 - Wrong user or password.";
        return false;
      }else {
        $message[] = "Found user <b>".$records[0]["cn"][0]." DN=".$records[0]["dn"]." </b>". print_r($records[0],true);
      }
    
    
      $entry = array();
      #seems a more correct way that handles complicated characters
      $entry["unicodePwd"] = iconv("UTF-8", "UTF-16LE", '"' . $newPassword . '"');
      # base64_encode is only needed in ldif text files !
      #$entry["unicodePwd"] = base64_encode($newPassw);
    
      $result = ldap_modify($con,$records[0]["dn"],$entry);
      if ($result === false){
        $message[] = $newpass.",".$entry["unicodePwd"]." Your password was not changed . with:".print_r($result, true);
        $error = ldap_error($con);
        $errno = ldap_errno($con);
        $message[] = "$errno - $error";
      }
      else {
        $message[] = " Your password has been changed. ";
        //mail($records[0]["mail"][0],"Password change notice : ".$user,"Your password has just been changed.");
        }
    ldap_close($con);
    }
    

    【讨论】:

    • 脚本没有经过全面测试,但既然你问如何,我使用 ldaps://
    • 使用 iconv("UTF-8", "UTF-16LE", '"' . $newPassword . '"') 似乎我有更好的结果
    【解决方案6】:

    我将 SAMBA 4.11 作为 AD/DC 运行,必须使用 ldap_modify_batch 才能使其正常工作。

    $修改 = [ [ “属性” => “unicodePwd”, “modtype” => LDAP_MODIFY_BATCH_REMOVE, "值" => [iconv("UTF-8", "UTF-16LE", '"' . $curPassword . '"')], ], [ “属性” => “unicodePwd”, "modtype" => LDAP_MODIFY_BATCH_ADD, "值" => [iconv("UTF-8", "UTF-16LE", '"' . $newPassword . '"')], ], ]; $result = ldap_modify_batch($ldapconn, $dn, $modifs); error_log("批量修改结果:".print_r($result, true)); $errorMessage = ldap_errno($ldapconn)." ". ldap_error($ldapconn); error_log("$errorMessage:".$errorMessage);

    【讨论】:

      猜你喜欢
      • 2012-06-26
      • 2013-02-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-17
      • 1970-01-01
      • 1970-01-01
      • 2011-12-02
      相关资源
      最近更新 更多