ActiveDirectoryでユーザーのパスワードを更新する簡単なソリューションに取り組んでいます。
ユーザーのパスワードを正常に更新できます。パスワードの更新は正常に機能します。ユーザーがパスワードを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ネットワーク認証に固有の特別な動作 と関係があります。
ValidateCredentials
インスタンスでPrincipalContext
メソッドを呼び出すと、安全なLDAP接続が確立され、続いて_ldap_bind_s
_関数呼び出しを使用してその接続でバインド操作が実行されます。
ValidateCredentials
を呼び出すときに使用される認証方法は_AuthType.Negotiate
_です。これを使用すると、Kerberosを使用してバインド操作が試行されます。これは(もちろんnotNTLMであるため)、上記の特別な動作を示しません。ただし、Kerberosを使用したバインドの試行は失敗し(パスワードが間違っていてすべて)、その結果、今回はNTLMを使用して別の試行が行われます。
これにアプローチする方法は2つあります。
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
がキャッシュされた資格情報を使用しないという事実を利用します。パスワードを現在の値に変更しようとすると、最初にパスワードが検証され、パスワードが正しくないか、ポリシーに問題があるか(同じパスワードを2回再利用できない)を判断できます。
注:これはおそらく、ポリシーに少なくとも最新のパスワードの繰り返しを許可しないという履歴要件がある場合にのみ機能します。
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
}
}
これを実行する方法のコンテキストによっては、「 キャッシュされた資格情報 」と呼ばれるものと関係がある場合があります。