validar usuario single password obtener datos crear conectar con active c# active-directory

c# - usuario - ¿Por qué Active Directory valida la última contraseña?



single sign on c# active directory (3)

Estoy trabajando en una solución simple para actualizar la contraseña de un usuario en Active Directory.

Puedo actualizar con éxito la contraseña de los usuarios. La actualización de la contraseña funciona bien. Digamos que el usuario ha actualizado la contraseña de MyPass1 a MyPass2

Ahora cuando ejecuto mi código personalizado para validar las credenciales de los usuarios usando:

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

Ahora cuando ingreso una contraseña incorrecta, valida muy bien:

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

Ahora, por alguna extraña razón, valida la última contraseña anterior que MyPass1 recuerda?

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

Obtuve este código de:

Validar un nombre de usuario y contraseña contra Active Directory?

¿Tiene algo que ver con la última expiración de contraseñas o así es como se supone que funciona la validación?


Dependiendo del contexto de cómo ejecute esto, puede tener que ver con algo llamado " credenciales en caché ".


He encontrado una forma de validar las credenciales actuales del usuario solamente. Aprovecha el hecho de que ChangePassword no usa credenciales en caché. Al intentar cambiar la contraseña a su valor actual, que primero valida la contraseña, podemos determinar si la contraseña es incorrecta o si hay un problema de política (no puede reutilizar la misma contraseña dos veces).

Nota: esto probablemente solo funcione si su política tiene un requisito de historial de al menos no permitir que se repita la contraseña más reciente.

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 } }


La razón por la que está viendo esto tiene que ver con un comportamiento especial específico para la autenticación de red NTLM .

Llamar al método ValidateCredentials en una instancia de PrincipalContext genera una conexión LDAP segura, seguida de una operación de vinculación que se realiza en esa conexión mediante una ldap_bind_s función ldap_bind_s .

El método de autenticación utilizado al llamar a ValidateCredentials es AuthType.Negotiate . El uso de esto da como resultado la operación de vinculación que se intenta con Kerberos, que (al no ser NTLM por supuesto) no exhibirá el comportamiento especial descrito anteriormente. Sin embargo, el intento de vinculación usando Kerberos fallará (la contraseña es incorrecta y todo), lo que dará como resultado que se realice otro intento, esta vez usando NTLM.

Tienes dos formas de acercarte a esto:

  1. Siga las instrucciones del artículo de Microsoft KB que he vinculado para acortar o eliminar el período de vigencia de una contraseña anterior utilizando el valor de registro OldPasswordAllowedPeriod . Probablemente no sea la solución más ideal.
  2. No use la clase PrincipleContext para validar las credenciales. Ahora que sabe (más o menos) cómo funciona ValidateCredentials , no debería ser demasiado difícil para usted hacer el proceso manualmente. Lo que querrá hacer es crear una nueva conexión LDAP ( LdapConnection ), establecer sus credenciales de red, establecer AuthType explícitamente en AuthType.Kerberos , y luego llamar a Bind() . Obtendrá una excepción si las credenciales son malas.

El siguiente código muestra cómo puede realizar la validación de credenciales utilizando solo Kerberos. El método de autenticación en uso no recurrirá a NTLM en caso de falla.

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; }

Intento nunca usar excepciones para manejar el control de flujo de mi código; sin embargo, en esta instancia particular, la única forma de probar las credenciales en una conexión LDAP parece ser intentar una operación de vinculación, que lanzará una excepción si las credenciales son malas. PrincipalContext tiene el mismo enfoque.