Как проверить учетные данные домена?



Я хочу проверить набор учетных данных для контроллера домена. например:



Username: STACKOVERFLOWjoel
Password: splotchy


Способ 1. Запрос Active Directory с олицетворением



многие люди предлагают запросить Active Directory для чего-то. Если возникает исключение, то вы знаете, что учетные данные недействительны - как это предлагается в этот вопрос stackoverflow.



есть некоторые серьезные недостатки однако:




  1. вы не только проверку подлинности с учетной записью домена, но вы делаете неявное авторизации. То есть Вы читаете свойства из объявления, используя маркер олицетворения. Что делать, если в противном случае действительный аккаунт не имеет прав на чтение из объявления? По умолчанию все пользователи имеют доступ на чтение, но политики домена можно настроить для отключения разрешений на доступ для ограниченных учетных записей (и / или групп).


  2. привязка к объявлению имеет серьезные накладные расходы, кэш схемы AD должен быть загружен на клиенте (кэш ADSI в поставщике ADSI, используемом DirectoryServices). Это и сеть, и рекламный сервер, потребляющий ресурсы-и слишком дорого для такой простой операции, как аутентификация учетной записи пользователя.


  3. вы полагаетесь на ошибку исключения для неисключительного случая и предполагаете, что это означает недопустимое имя пользователя и пароль. Другие проблемы (например, сбой сети, сбой подключения AD, ошибка выделения памяти и т. д.) Затем неверно интерпретируются как ошибка аутентификации.



Способ 2. LogonUser Win32 API



другие предложили использовать LogonUser() функции API. Это звучит неплохо, но, к сожалению, вызывающий пользователь иногда нуждается в разрешении, обычно предоставляемом только самой операционной системе:




процесс вызова LogonUser требует
привилегия SE_TCB_NAME. Если
вызывающий процесс не имеет этого
привилегия, LogonUser терпит неудачу и
GetLastError возвращает
ERROR_PRIVILEGE_NOT_HELD.



В некоторых
случаи, процесс, который вызывает
LogonUser также должен иметь
Привилегия SE_CHANGE_NOTIFY_NAME
включено; в противном случае LogonUser завершается ошибкой
и getlasterror возвращает
Результат error_access_denied. Эта привилегия является
не требуется для локальной системы
учетная запись или учетные записи, являющиеся участниками
группы "администраторы". От
по умолчанию, SE_CHANGE_NOTIFY_NAME является
включена для всех пользователей, но некоторые
администраторы могут отключить его для
каждый.




раздавать "действовать как часть операционной системы" привилегия-это не то, что вы хотите сделать волей-неволей - как указывает Microsoft в статьи базы знаний:




...процесс, который вызывает
LogonUser должен иметь имя SE_TCB_NAME
привилегия (в Диспетчере пользователей это
в "действовать как часть операционной
Система
" справа). В SE_TCB_NAME
привилегия очень мощная и
не должны быть предоставлены любому произвольному пользователю только для того, чтобы они могли
запустите приложение
что нужно
проверьте учетные данные.




кроме того, вызов LogonUser() будет выполнена, если указан пустой пароль.





Как правильно аутентифицировать набор учетных данных домена?





Я произойдет для вызова из управляемого код, но это общий вопрос Windows. Можно предположить, что у клиентов установлена платформа .NET Framework 2.0.

651   5  

5 ответов:

C# в .NET 3.5 с помощью

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security;
using System.DirectoryServices.AccountManagement;

public struct Credentials
{
    public string Username;
    public string Password;
}

public class Domain_Authentication
{
    public Credentials Credentials;
    public string Domain;

    public Domain_Authentication(string Username, string Password, string SDomain)
    {
        Credentials.Username = Username;
        Credentials.Password = Password;
        Domain = SDomain;
    }

    public bool IsValid()
    {
        using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, Domain))
        {
            // validate the credentials
            return pc.ValidateCredentials(Credentials.Username, Credentials.Password);
        }
    }
}

Я использую следующий код для проверки учетных данных. Метод, показанный ниже, подтвердит правильность учетных данных, а если нет, то пароль истек или нуждается в изменении.

Я искал что-то вроде этого в течение веков... Так что я надеюсь, что это кому-то поможет!

using System;
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
using System.Runtime.InteropServices;

namespace User
{
    public static class UserValidation
    {
        [DllImport("advapi32.dll", SetLastError = true)]
        static extern bool LogonUser(string principal, string authority, string password, LogonTypes logonType, LogonProviders logonProvider, out IntPtr token);
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool CloseHandle(IntPtr handle);
        enum LogonProviders : uint
        {
            Default = 0, // default for platform (use this!)
            WinNT35,     // sends smoke signals to authority
            WinNT40,     // uses NTLM
            WinNT50      // negotiates Kerb or NTLM
        }
        enum LogonTypes : uint
        {
            Interactive = 2,
            Network = 3,
            Batch = 4,
            Service = 5,
            Unlock = 7,
            NetworkCleartext = 8,
            NewCredentials = 9
        }
        public  const int ERROR_PASSWORD_MUST_CHANGE = 1907;
        public  const int ERROR_LOGON_FAILURE = 1326;
        public  const int ERROR_ACCOUNT_RESTRICTION = 1327;
        public  const int ERROR_ACCOUNT_DISABLED = 1331;
        public  const int ERROR_INVALID_LOGON_HOURS = 1328;
        public  const int ERROR_NO_LOGON_SERVERS = 1311;
        public  const int ERROR_INVALID_WORKSTATION = 1329;
        public  const int ERROR_ACCOUNT_LOCKED_OUT = 1909;      //It gives this error if the account is locked, REGARDLESS OF WHETHER VALID CREDENTIALS WERE PROVIDED!!!
        public  const int ERROR_ACCOUNT_EXPIRED = 1793;
        public  const int ERROR_PASSWORD_EXPIRED = 1330;

        public static int CheckUserLogon(string username, string password, string domain_fqdn)
        {
            int errorCode = 0;
            using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domain_fqdn, "ADMIN_USER", "PASSWORD"))
            {
                if (!pc.ValidateCredentials(username, password))
                {
                    IntPtr token = new IntPtr();
                    try
                    {
                        if (!LogonUser(username, domain_fqdn, password, LogonTypes.Network, LogonProviders.Default, out token))
                        {
                            errorCode = Marshal.GetLastWin32Error();
                        }
                    }
                    catch (Exception)
                    {
                        throw;
                    }
                    finally
                    {
                        CloseHandle(token);
                    }
                }
            }
            return errorCode;
        }
    }

вот как определить локального пользователя:

    public bool IsLocalUser()
    {
        return windowsIdentity.AuthenticationType == "NTLM";
    }

Edit by Ian Boyd

вы не должны использовать NTLM больше вообще. Он настолько стар и настолько плох, что верификатор приложений Microsoft (который используется для обнаружения распространенных ошибок программирования) выдаст предупреждение, если он обнаружит, что вы используете NTLM.

вот глава из документации по верификатору приложений о том, почему у них есть тест, если кто-то ошибочно использует NTLM:

зачем нужен плагин NTLM

NTLM-это устаревший протокол аутентификации с недостатками, которые потенциально поставить под угрозу безопасность приложений и операционной системы система. Самым главным недостатком является отсутствие сервера аутентификация, которая может позволить злоумышленнику обмануть пользователей подключение к поддельному серверу. Как следствие отсутствия сервера аутентификация, приложения, использующие NTLM также могут быть уязвимы для тип атаки, известный как "отражение" атаки. Это последнее позволяет злоумышленник для захвата диалога аутентификации пользователя в a легитимный сервер и использовать его для аутентификации злоумышленника на компьютер пользователя. Уязвимости NTLM и способы их использования являются ли целью повышения исследовательской активности в области безопасности сообщество.

хотя Kerberos был доступен в течение многих лет многие приложения по-прежнему пишутся только для использования NTLM. Это напрасно уменьшает безопасность приложения. Однако Kerberos не может заменить NTLM во всех сценарии – в основном те, где клиент должен пройти проверку подлинности системы, которые не присоединены к домену (возможно, домашняя сеть самые распространенные из них). Пакет безопасности Negotiate позволяет обратная совместимость компромисс, который использует Kerberos, когда это возможно и только возвращается к NTLM, когда нет другого варианта. Код переключения чтобы использовать переговоры вместо NTLM будет значительно увеличить безопасность наши клиенты пока вводящ немногие или никакое применение совместимости. Переговоры сами по себе не серебряная пуля-есть есть случаи, когда злоумышленник может принудительно понизить рейтинг до NTLM, но это значительно сложнее в эксплуатации. Однако, одно немедленное улучшение заключается в том, что приложения, написанные для использования Negotiate правильно автоматически иммунны к атакам отражения NTLM.

в качестве последнего слова предостережения против использования NTLM:в будущем варианты Windows можно будет отключить использование NTLM по адресу операционная система. Если приложения имеют жесткую зависимость от NTLM они просто не смогут аутентифицироваться, когда NTLM отключен.

как работает плагин

вилка верификатора обнаруживает следующие ошибки:

  • пакет NTLM непосредственно указывается в вызове AcquireCredentialsHandle (или API оболочки более высокого уровня).

  • имя цели в вызове InitializeSecurityContext равно NULL.

  • целевое имя в вызове InitializeSecurityContext не является правильно сформированным доменным именем SPN, UPN или NetBIOS-стиля.

последние два случая заставят Negotiate вернуться к NTLM либо напрямую (первый случай), либо косвенно (контроллер домена вернет ошибку "основной не найден" во втором случае, вызывая падение Negotiate спина.)

плагин также регистрирует предупреждения при обнаружении понижений до NTLM; например, когда SPN не найден контроллером домена. Они регистрируются только как предупреждения, поскольку они часто являются законными случаями – например, при проверке подлинности в системе, которая не присоединена к домену.

NTLM останавливается

5000-приложение явно выбрало пакет NTLM

Уровень Важности – Ошибка

в приложения или подсистемы явно выбирает NTLM вместо того, чтобы договариваться в вызове AcquireCredentialsHandle. Несмотря на то, что клиент и сервер могут аутентифицироваться с помощью Kerberos, это предотвращается явным выбором NTLM.

как исправить эту ошибку

исправление этой ошибки заключается в выборе пакета согласования вместо NTLM. Как это будет сделано, будет зависеть от конкретной сетевой подсистемы, используемой клиент или сервер. Некоторые примеры приведены ниже. Вы должны ознакомиться с документацией по конкретной библиотеке или набору API, который вы используете.

APIs(parameter) Used by Application    Incorrect Value  Correct Value  
=====================================  ===============  ========================
AcquireCredentialsHandle (pszPackage)  “NTLM”           NEGOSSP_NAME “Negotiate”
using System;
using System.Collections.Generic;
using System.Text;
using System.DirectoryServices.AccountManagement;

class WindowsCred
{
    private const string SPLIT_1 = "\";

    public static bool ValidateW(string UserName, string Password)
    {
        bool valid = false;
        string Domain = "";

        if (UserName.IndexOf("\") != -1)
        {
            string[] arrT = UserName.Split(SPLIT_1[0]);
            Domain = arrT[0];
            UserName = arrT[1];
        }

        if (Domain.Length == 0)
        {
            Domain = System.Environment.MachineName;
        }

        using (PrincipalContext context = new PrincipalContext(ContextType.Domain, Domain)) 
        {
            valid = context.ValidateCredentials(UserName, Password);
        }

        return valid;
    }
}

Кашиф Муштак Оттава, Канада

Comments

    Ничего не найдено.