2012-01-21 14 views
32

Sto lavorando a una soluzione semplice per aggiornare la password di un utente in Active Directory.Perché Active Directory convalida l'ultima password?

Posso aggiornare correttamente la password dell'utente. L'aggiornamento della password funziona correttamente. Diciamo che l'utente ha aggiornato la password dal MyPass1 a MyPass2

Ora, quando eseguo il mio codice personalizzato per convalidare gli utenti delle credenziali utilizzando:

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

//returns true - which is good 

Ora quando entro qualche password errata convalida molto bene:

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

//returns false - which is good 

Ora per alcuni motivi dispari, convalida l'ultima password precedente che era MyPass1 ricordi?

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 

ho ottenuto questo codice:

Validate a username and password against Active Directory?

è qualcosa a che fare con ultima password scadenza o è questo il modo la convalida dovrebbe funzionare?

+0

Quanto è grande l'infrastruttura del dominio? È possibile che si connetta a una CC differente ogni volta e che la nuova password non sia stata replicata. –

+0

Dopo aver aggiornato la password a una nuova, non riesco ad accedere utilizzando la vecchia password che è buona. Il suo solo quando uso le ValidateCredentials() metodo, i suoi Restituisce vero per la vecchia password :( forse qualcosa a che fare con le credenziali memorizzate nella cache? – theITvideos

risposta

1

A seconda del contesto in cui viene eseguito, potrebbe trattarsi di qualcosa chiamato "cached credentials".

+0

se si tratta di qualcosa a che fare con le credenziali memorizzate nella cache ... come posso cancellare la cache della password tramite C# – theITvideos

45

Il motivo per cui stai vedendo questo ha a che fare con special behavior specific to NTLM network authentication.

Chiamando il metodo ValidateCredentials sulla PrincipalContext istanza determina una connessione LDAP protetta compiuti, seguita da un'operazione di associazione corso a tale connessione tramite una chiamata ldap_bind_s funzione.

Il metodo di autenticazione utilizzato quando si chiama ValidateCredentials è AuthType.Negotiate. Utilizzando questo risultato l'operazione di bind viene tentata utilizzando Kerberos, che (essendo non NTLM ovviamente) non mostrerà il comportamento speciale sopra descritto. Tuttavia, il tentativo di bind con Kerberos fallirà (la password è sbagliata e tutto), il che comporterà un altro tentativo, questa volta utilizzando NTLM.

ci sono due modi per affrontare questo:

  1. seguire le istruzioni riportate nell'articolo Microsoft KB ho collegato a ridurre o eliminare il periodo di durata di una vecchia password utilizzando il valore del Registro OldPasswordAllowedPeriod. Probabilmente non è la soluzione più ideale.
  2. Non utilizzare la classe PrincipleContext per convalidare le credenziali. Ora che sai (approssimativamente) come funziona ValidateCredentials, non dovrebbe essere troppo difficile per te eseguire il processo manualmente. Quello che vorrete fare è creare una nuova connessione LDAP (LdapConnection), impostare le sue credenziali di rete, impostare l'AuthType esplicitamente su AuthType.Kerberos e quindi chiamare Bind(). Otterrai un'eccezione se le credenziali sono cattive.

Il codice seguente mostra come è possibile eseguire la convalida delle credenziali utilizzando solo Kerberos. Il metodo di autenticazione in uso non ricadrà su NTLM in caso di errore.

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

Cerco di non utilizzare mai eccezioni per gestire il controllo di flusso del mio codice; tuttavia, in questa particolare istanza, l'unico modo per verificare le credenziali su una connessione LDAP sembra essere quello di tentare un'operazione Bind, che genererà un'eccezione se le credenziali sono negative. PrincipalContext ha lo stesso approccio.

+0

Grazie per la risposta Ho due domande riguardanti i 2 approcci – theITvideos

+0

1) È possibile aggiornare "OldPasswordAllowedPeriod" tramite C# o DIRECTORY IDE senza toccare il registro? 2) Quindi, secondo l'articolo di Microsoft, il tempo predefinito in minuti per OldPasswordAllowedPeriod è di 60 minuti ... c'è un modo in Active Directory per confermare questo. 3) Puoi suggerire un codice C# che convalida l'utente utilizzando il metodo AuthType.Kerberos. Prendendo i parametri username e password. Si prega di rispondere Grazie. – theITvideos

+0

Ho aggiunto qualche codice di esempio. Non sono a conoscenza di alcun modo per impostare il valore di registro 'OldPasswordAllowedPeriod' al di fuori dell'utilizzo dell'API del Registro di sistema standard. –

1

Ho trovato un modo per convalidare solo le credenziali correnti dell'utente. Sfrutta il fatto che lo ChangePassword non utilizzi le credenziali memorizzate nella cache. Tentando di modificare la password al suo valore corrente, che prima convalida la password, possiamo determinare se la password è errata o se esiste un problema di politica (non è possibile riutilizzare la stessa password due volte).

Nota: questo probabilmente funzionerà solo se la vostra politica ha un requisito di cronologia almeno che non consente di ripetere la password più recente.

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