Chcę zweryfikować zestaw poświadczeń względem kontrolera domeny. na przykład:
Username: STACKOVERFLOW\joel
Password: splotchy
Metoda 1. Zapytanie do usługi Active Directory z personifikacją
Wiele osób sugeruje wyszukanie czegoś w usłudze Active Directory. Jeśli zostanie zgłoszony wyjątek, to wiesz, że poświadczenia są nieprawidłowe - jak sugeruje to pytanie o przepływie stosu .
Takie podejście ma jednak kilka poważnych wad :
Nie tylko uwierzytelniasz konto domeny, ale również przeprowadzasz niejawną kontrolę autoryzacji. Oznacza to, że odczytujesz właściwości z usługi AD przy użyciu tokenu personifikacji. Co się stanie, jeśli konto, które w przeciwnym razie działa, nie ma uprawnień do odczytu z reklamy? Domyślnie wszyscy użytkownicy mają dostęp do odczytu, ale zasady domeny można ustawić tak, aby wyłączyć uprawnienia dostępu dla kont z ograniczeniami (i / lub grup).
Powiązanie z usługą AD wiąże się z poważnymi narzutami, pamięć podręczna schematu usługi AD musi zostać załadowana po stronie klienta (pamięć podręczna ADSI u dostawcy ADSI używana przez DirectoryServices). Jest to zarówno sieć, jak i serwer AD, które pochłaniają zasoby - i są zbyt kosztowne dla prostej operacji, takiej jak uwierzytelnianie konta użytkownika.
Polegasz na niepowodzeniu wyjątku w przypadku nietypowym i zakładasz, że oznacza to nieprawidłową nazwę użytkownika i hasło. Inne problemy (np. Awaria sieci, awaria łączności AD, błąd alokacji pamięci itp.) Są następnie błędnie interpretowane jako awaria uwierzytelniania.
Metoda 2. LogonUser Win32 API
Inni sugerowali użycie LogonUser()
funkcji API. Brzmi to nieźle, ale niestety użytkownik wywołujący czasami potrzebuje pozwolenia, które zwykle jest udzielane tylko samemu systemowi operacyjnemu:
Proces wywołujący LogonUser wymaga uprawnienia SE_TCB_NAME. Jeśli proces wywołujący nie ma tego uprawnienia, LogonUser kończy się niepowodzeniem i GetLastError zwraca ERROR_PRIVILEGE_NOT_HELD.
W niektórych przypadkach proces, który wywołuje LogonUser, musi mieć również włączone uprawnienie SE_CHANGE_NOTIFY_NAME; w przeciwnym razie LogonUser nie powiedzie się i GetLastError zwraca ERROR_ACCESS_DENIED. To uprawnienie nie jest wymagane w przypadku lokalnego konta systemowego ani kont należących do grupy administratorów. Domyślnie SE_CHANGE_NOTIFY_NAME jest włączony dla wszystkich użytkowników, ale niektórzy administratorzy mogą go wyłączyć dla wszystkich.
Udostępnianie uprawnienia „ Działaj jako część systemu operacyjnego ” nie jest czymś, co chcesz robić, kiedy nie chcesz - jak wskazuje Microsoft w artykule z bazy wiedzy :
... proces, który wywołuje LogonUser, musi mieć uprawnienie SE_TCB_NAME (w Menedżerze użytkowników jest to uprawnienie „ Działaj jako część systemu operacyjnego ”). Przywilej SE_TCB_NAME ma bardzo duże możliwości i nie powinien być nadawany żadnemu dowolnemu użytkownikowi tylko po to, aby mógł on uruchomić aplikację wymagającą weryfikacji poświadczeń.
Ponadto wywołanie LogonUser()
nie powiedzie się, jeśli zostanie określone puste hasło.
Jaki jest właściwy sposób uwierzytelniania zestawu poświadczeń domeny?
Tak się składa, że dzwonię z kodu zarządzanego, ale jest to ogólne pytanie dotyczące systemu Windows. Można założyć, że klienci mają zainstalowany .NET Framework 2.0.
źródło
Odpowiedzi:
C # w .NET 3.5 przy użyciu System.DirectoryServices.AccountManagement .
bool valid = false; using (PrincipalContext context = new PrincipalContext(ContextType.Domain)) { valid = context.ValidateCredentials( username, password ); }
Spowoduje to weryfikację w bieżącej domenie. Sprawdź sparametryzowany konstruktor PrincipalContext, aby uzyskać inne opcje.
źródło
new PrincipalContext(ContextType.Machine)
zamiast tego użyłeś .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); } } }
źródło
Domain
parametr podczas tworzeniaPrincipalContext
, coś, co mnie interesowało i znalazłem w tej odpowiedzi.Używam następującego kodu do weryfikacji poświadczeń. Przedstawiona poniżej metoda potwierdzi, czy poświadczenia są poprawne, a jeśli nie, to hasło wygasło lub wymaga zmiany.
Szukałem czegoś takiego od wieków ... Mam nadzieję, że to komuś pomoże!
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; } }
źródło
Oto jak określić użytkownika lokalnego:
public bool IsLocalUser() { return windowsIdentity.AuthenticationType == "NTLM"; }
Edycja przez Iana Boyda
Nie powinieneś już w ogóle używać NTLM. Jest tak stary i tak zły, że weryfikator aplikacji firmy Microsoft (który służy do wychwytywania typowych błędów programistycznych) wyświetli ostrzeżenie, jeśli wykryje, że używasz NTLM.
Oto rozdział z dokumentacji Application Verifier o tym, dlaczego mają test, jeśli ktoś omyłkowo używa NTLM:
źródło
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; } }
Kashif Mushtaq Ottawa, Kanada
źródło