Почему Active Directory проверяет последний пароль? - PullRequest
33 голосов
/ 21 января 2012

Я работаю над простым решением для обновления пароля пользователя в 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?

Это как-то связано с истечением срока действия последнего пароля или так должна работать проверка?

Ответы [ 3 ]

52 голосов
/ 21 января 2012

Причина, по которой вы видите это, связана с особым поведением, специфичным для сетевой аутентификации NTLM .

Вызов метода ValidateCredentials в экземпляре PrincipalContext приводит к безопасномуСоединение LDAP устанавливается, после чего выполняется операция связывания этого соединения с использованием вызова функции ldap_bind_s.

Метод аутентификации, используемый при вызове ValidateCredentials: AuthType.Negotiate.Использование этого приводит к попытке операции связывания с использованием Kerberos, которая (будучи, конечно, , а не NTLM) не будет демонстрировать особого поведения, описанного выше.Однако попытка связывания с использованием Kerberos завершится неудачно (неверный пароль и все), что приведет к другой попытке, на этот раз с использованием NTLM.

У вас есть два способа приблизиться к этому:

  1. Следуйте инструкциям в статье Microsoft KB, которую я связал, чтобы сократить или исключить срок жизни старого пароля, используя значение реестра OldPasswordAllowedPeriod .Наверное, не самое идеальное решение.
  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, по-видимому, является попытка выполнить операцию Bind, которая выдает исключение, если учетные данные неверны.PrincipalContext использует тот же подход.

1 голос
/ 01 октября 2015

Я нашел способ проверить только текущие учетные данные пользователя. Он использует тот факт, что 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
            }
        }
1 голос
/ 21 января 2012

В зависимости от контекста того, как вы это выполняете, это может иметь отношение к тому, что называется " кэшированные учетные данные ."

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...