c# - 为什么 Active Directory 会验证最后一个密码?

标签 c# active-directory

我正在研究一个简单的解决方案来更新 Active Directory 中的用户密码。

我可以成功更新用户密码。更新密码工作正常。假设用户已将密码从 MyPass1 更新为 MyPass2

现在,当我运行自定义代码以使用以下方法验证用户凭据时:

using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "TheDomain"))
{
    // validate the credentials
    bool isValid = pc.ValidateCredentials("myuser", "MyPass2");
}

//returns true - which is good

现在,当我输入一些错误的密码时,它会很好地验证:

using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "TheDomain"))
{
    // validate the credentials
    bool isValid = pc.ValidateCredentials("myuser", "wrongPass");
}

//returns false - which is good

现在由于一些奇怪的原因,它验证了之前的最后一个密码,也就是 MyPass1,还记得吗?

using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "TheDomain"))
{
    // validate the credentials
    bool isValid = pc.ValidateCredentials("myuser", "MyPass1");
}

//returns true - but why? we have updated password to Mypass2

我从以下位置获得此代码:

Validate a username and password against Active Directory?

这是否与上次密码过期有关,或者验证应该如何工作?

最佳答案

你看到这个的原因与 special behavior specific to NTLM network authentication 有关.

PrincipalContext 实例上调用 ValidateCredentials 方法会导致建立安全的 LDAP 连接,然后使用 ldap_bind_s 在该连接上执行绑定(bind)操作 函数调用。

调用ValidateCredentials时使用的身份验证方法是AuthType.Negotiate。使用它会导致尝试使用 Kerberos 进行绑定(bind)操作,Kerberos(当然不是 NTLM)不会表现出上述特殊行为。但是,使用 Kerberos 的绑定(bind)尝试将失败(密码错误和全部),这将导致进行另一次尝试,这次使用 NTLM。

您有两种方法可以解决此问题:

  1. 按照我链接的 Microsoft 知识库文章中的说明使用 OldPasswordAllowedPeriod 注册表值缩短或消除旧密码的生命周期。可能不是最理想的解决方案。
  2. 不要使用 PrincipleContext 类来验证凭据。既然您已经(大致)了解了 ValidateCredentials 的工作原理,那么手动完成该过程应该不会太困难。您要做的是创建一个新的 LDAP 连接 (LdapConnection),设置其网络凭据,将 AuthType 显式设置为 AuthType.Kerberos,然后调用 绑定(bind)()。如果凭据错误,您将得到一个异常(exception)。

以下代码显示了如何仅使用 Kerberos 执行凭据验证。如果失败,所使用的身份验证方法将不会回退到 NTLM。

private const int ERROR_LOGON_FAILURE = 0x31;

private bool ValidateCredentials(string username, string password, string domain)
{
  NetworkCredential credentials
    = new NetworkCredential(username, password, domain);

  LdapDirectoryIdentifier id = new LdapDirectoryIdentifier(domain);

  using (LdapConnection connection = new LdapConnection(id, credentials, AuthType.Kerberos))
  {
    connection.SessionOptions.Sealing = true;
    connection.SessionOptions.Signing = true;

    try
    {
      connection.Bind();
    }
    catch (LdapException lEx)
    {
      if (ERROR_LOGON_FAILURE == lEx.ErrorCode)
      {
        return false;
      }
      throw;
    }
  }
  return true;
}

我尽量不使用异常来处理代码的流程控制;但是,在这个特定的实例中,测试 LDAP 连接上的凭据的唯一方法似乎是尝试绑定(bind)操作,如果凭据错误,这将引发异常。 PrincipalContext 采用相同的方法。

关于c# - 为什么 Active Directory 会验证最后一个密码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8949501/

相关文章:

C# 在 Active Directory 中创建 OU

java - KDC - 我们从哪里获得服务器?

python - 通过 python-ldap 使用 Active Directory 中的 unicode 编码字符串

c# - 对用户单击已选择的ListViewItem,MVVM使用react

c# - 可以在 Windows 中以编程方式识别设备的物理 USB 端口吗?

powershell - 如何使用 PowerShell 为 AD 组中的每个人检索电子邮件别名和 CN

c# - 返回用户所属的所有 Active Directory 应用程序组的列表

c# - 如何设计一个包含查询表中的属性的类?

c# - 如何使用 DI 通过 IValidatableObject 接口(interface)向实体提供验证器?

c# - MonoDevelop 和/或 MonoTouch 错误?