问题描述
我正在研究一个简单的解决方案来更新 Active Directory 中的用户密码.
I am working on a simple solution to update a user's password in Active Directory.
我可以成功更新用户密码.更新密码工作正常.假设用户已将密码从 MyPass1 更新为 MyPass2
I can successfully update the users password. Updating the password works fine. Lets say the user has updated the password from MyPass1 to MyPass2
现在,当我运行自定义代码以使用以下方法验证用户凭据时:
Now when I run my custom code to validate users credential using:
using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "TheDomain"))
{
// validate the credentials
bool isValid = pc.ValidateCredentials("myuser", "MyPass2");
}
//returns true - which is good
现在当我输入一些错误的密码时,它会很好地验证:
Now when I enter some wrong password it validates very nicely:
using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "TheDomain"))
{
// validate the credentials
bool isValid = pc.ValidateCredentials("myuser", "wrongPass");
}
//returns false - which is good
现在由于一些奇怪的原因,它验证了 MyPass1 记得的上一个密码?
Now for some odd reasons, it validates the previous last password which was MyPass1 remember?
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
我从以下位置获得此代码:
I got this code from:
针对 Active Directory 验证用户名和密码?
是否与上次密码到期有关,还是验证应该如何工作?
Is it something to do with last password expiry or is this how the validation supposed to work?
推荐答案
您看到此内容的原因与 特定于 NTLM 网络身份验证的特殊行为.
The reason why you are seeing this has to do with special behavior specific to NTLM network authentication.
在 PrincipalContext
实例上调用 ValidateCredentials
方法会导致建立安全的 LDAP 连接,然后使用 ldap_bind_s 在该连接上执行绑定操作
函数调用.
Calling the ValidateCredentials
method on a PrincipalContext
instance results in a secure LDAP connection being made, followed by a bind operation being performed on that connection using a ldap_bind_s
function call.
调用ValidateCredentials
时使用的认证方法是AuthType.Negotiate
.使用此结果会导致尝试使用 Kerberos 进行绑定操作,这(当然是不是 NTLM)不会表现出上述特殊行为.但是,使用 Kerberos 的绑定尝试将失败(密码错误等等),这将导致再次尝试使用 NTLM.
The authentication method used when calling ValidateCredentials
is AuthType.Negotiate
. Using this results in the bind operation being attempted using Kerberos, which (being not NTLM of course) will not exhibit the special behavior described above. However, the bind attempt using Kerberos will fail (the password being wrong and all), which will result in another attempt being made, this time using NTLM.
您有两种方法可以解决这个问题:
You have two ways to approach this:
- 按照我链接的 Microsoft 知识库文章中的说明使用 OldPasswordAllowedPeriod 注册表值缩短或消除旧密码的生命周期.可能不是最理想的解决方案.
- 不要使用
PrincipleContext
类来验证凭据.既然您(大致)了解了ValidateCredentials
的工作原理,那么手动执行该过程应该不会太困难.您要做的是创建一个新的 LDAP 连接 (LdapConnection
),设置其网络凭据,将 AuthType 显式设置为AuthType.Kerberos
,然后调用绑定()
.如果凭据不好,您将收到异常.
- Follow the instructions in the Microsoft KB article I linked to shorten or eliminate the lifetime period of an old password using the OldPasswordAllowedPeriod registry value. Probably not the most ideal solution.
- Don't use
PrincipleContext
class to validate credentials. Now that you know (roughly) howValidateCredentials
works, it shouldn't be too difficult for you to do the process manually. What you'll want to do is create a new LDAP connection (LdapConnection
), set its network credentials, set the AuthType explicitly toAuthType.Kerberos
, and then callBind()
. You'll get an exception if the credentials are bad.
以下代码显示了如何仅使用 Kerberos 执行凭据验证.使用中的身份验证方法在失败时不会回退到 NTLM.
The following code shows how you can perform credential validation using only Kerberos. The authentication method at use will not fall back to NTLM in the event of failure.
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
采用相同的方法.
I try to never use exceptions to handle the flow control of my code; however, in this particular instance, the only way to test credentials on a LDAP connection seems to be to attempt a Bind operation, which will throw an exception if the credentials are bad. PrincipalContext
takes the same approach.
这篇关于为什么 Active Directory 验证最后一个密码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!