为什么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

我从这里得到了这段代码:

根据Active Directory验证用户名和密码?

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


您看到这种情况的原因与特定于NTLM网络身份验证的特殊行为有关。

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

调用ValidateCredentials时使用的身份验证方法是AuthType.Negotiate 。 使用这个结果会导致正在尝试使用Kerberos的绑定操作,该Kerberos(当然不是NTLM)不会显示上述特殊行为。 但是,使用Kerberos的绑定尝试将失败(密码错误且全部),这将导致另一次尝试,这次使用NTLM。

你有两种方法来解决这个问题:

  • 按照我链接的Microsoft知识库文章中的说明,使用OldPasswordAllowedPeriod注册表值缩短或消除旧密码的生存期。 可能不是最理想的解决方案。
  • 不要使用PrincipleContext类来验证凭据。 既然您已经(大致)了解了ValidateCredentials工作原理,那么您手动执行该过程应该不会太困难。 你需要做的是创建一个新的LDAP连接( LdapConnection ),设置其网络凭据,将AuthType显式设置为AuthType.Kerberos ,然后调用Bind() 。 如果证书不好,你会得到一个异常。
  • 以下代码显示了如何仅使用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连接上凭据的唯一方法似乎是尝试绑定操作,如果证书不正确,将会引发异常。 PrincipalContext采用相同的方法。


    根据你如何运行它的上下文,它可能需要处理一些叫做“缓存凭据”的东西。


    我找到了一种验证用户当前凭据的方法。 它利用了ChangePassword不使用缓存凭据的事实。 通过尝试将密码更改为当前值(首先验证密码),我们可以确定密码是不正确的还是存在策略问题(无法重复使用相同的密码两次)。

    请注意:如果您的保单的历史记录要求至少不允许重复最近的密码,则这可能只适用。

            var isPasswordValid = PrincipalContext.ValidateCredentials(
                userName,
                password);
    
            // use ChangePassword to test credentials as it doesn't use caching, unlike ValidateCredentials
            if (isPasswordValid)
            {
                try
                {
                    user.ChangePassword(password, password);
                }
                catch (PasswordException ex)
                {
                    if (ex.InnerException != null && ex.InnerException.HResult == -2147024810)
                    {
                        // Password is wrong - must be using a cached password
                        isPasswordValid = false;
                    }
                    else
                    {
                        // Password policy problem - this is expected, as we can't change a password to itself for history reasons    
                    }
                }
                catch (Exception)
                {
                    // ignored, we only want to check wrong password. Other AD related exceptions should occure in ValidateCredentials
                }
            }
    
    链接地址: http://www.djcxy.com/p/36073.html

    上一篇: Why does Active Directory validate last password?

    下一篇: Get local IP address