Etki alanı kimlik bilgileri nasıl doğrulanır?


87

Etki alanı denetleyicisine göre bir dizi kimlik bilgisini doğrulamak istiyorum. Örneğin:

Username: STACKOVERFLOW\joel
Password: splotchy

Yöntem 1. Kimliğe Bürünme ile Active Directory'yi Sorgulama

Birçok kişi bir şey için Active Directory'yi sorgulamanızı önerir. Bir istisna atılırsa, bu yığın akışı sorusunda önerildiği gibi kimlik bilgilerinin geçerli olmadığını anlarsınız .

Ancak bu yaklaşımın bazı ciddi dezavantajları vardır :

  1. Yalnızca bir etki alanı hesabının kimliğini doğrulamıyorsunuz, aynı zamanda örtük bir yetkilendirme kontrolü de yapıyorsunuz. Yani, kimliğe bürünme belirteci kullanarak AD'deki özellikleri okuyorsunuz. Aksi halde geçerli olan hesabın AD'den okuma hakkı yoksa ne olur? Varsayılan olarak tüm kullanıcıların okuma erişimi vardır, ancak etki alanı politikaları, kısıtlanmış hesaplar (ve veya gruplar) için erişim izinlerini devre dışı bırakacak şekilde ayarlanabilir.

  2. AD'ye bağlanmanın ciddi bir ek yükü vardır, AD şeması önbelleğinin istemcide yüklenmesi gerekir (DirectoryServices tarafından kullanılan ADSI sağlayıcısındaki ADSI önbelleği). Bu hem ağ hem de AD sunucusu, kaynak tüketir ve bir kullanıcı hesabının kimlik doğrulaması gibi basit bir işlem için çok pahalıdır.

  3. İstisnai olmayan bir durum için bir istisna hatasına güveniyorsunuz ve bunun geçersiz kullanıcı adı ve şifre anlamına geldiğini varsayıyorsunuz. Diğer sorunlar (ör. Ağ hatası, AD bağlantı hatası, bellek ayırma hatası, vb.) Daha sonra kimlik doğrulama hatası olarak yanlış yorumlanır.

Yöntem 2. LogonUser Win32 API

DiğerleriLogonUser() API işlevini kullanmayı önerdi . Kulağa hoş geliyor, ancak ne yazık ki arayan kullanıcının bazen yalnızca işletim sistemine verilen bir izne ihtiyacı var:

LogonUser'ı çağıran işlem SE_TCB_NAME ayrıcalığını gerektirir. Çağıran işlem bu ayrıcalığa sahip değilse, LogonUser başarısız olur ve GetLastError ERROR_PRIVILEGE_NOT_HELD döndürür.

Bazı durumlarda, LogonUser'ı çağıran işlemin SE_CHANGE_NOTIFY_NAME ayrıcalığının da etkinleştirilmiş olması gerekir; aksi halde LogonUser başarısız olur ve GetLastError ERROR_ACCESS_DENIED döndürür. Bu ayrıcalık, yerel sistem hesabı veya yöneticiler grubunun üyesi olan hesaplar için gerekli değildir. Varsayılan olarak, SE_CHANGE_NOTIFY_NAME tüm kullanıcılar için etkindir, ancak bazı yöneticiler bunu herkes için devre dışı bırakabilir.

"Dışarı Asma işletim sisteminin bir parçası olarak Yasası " ayrıcalık sen ister istemez yapmak istediğim bir şey değildir - Microsoft işaret ettiği gibi bilgi bankası makalesinde :

... LogonUser'ı çağıran işlem SE_TCB_NAME ayrıcalığına sahip olmalıdır (Kullanıcı Yöneticisi'nde bu, " İşletim Sisteminin bir parçası olarak hareket et " hakkıdır). SE_TCB_NAME ayrıcalığı çok güçlüdür ve herhangi bir rastgele kullanıcıya, kimlik bilgilerini doğrulaması gereken bir uygulamayı çalıştırabilmeleri için verilmemelidir .

Ayrıca, LogonUser()boş bir parola belirlenirse bir çağrı başarısız olacaktır.


Bir dizi etki alanı kimlik bilgisini doğrulamanın doğru yolu nedir?


Ben ne yönetilen koddan araman için, ancak bu aa genel, Windows sorudur. Müşterilerin .NET Framework 2.0'ın yüklü olduğu varsayılabilir.


1
Okuyucular, Windows XP'den itibaren LogonUser'ın artık SE_TCB_NAME gerektirmediğini unutmamalıdır (bir Passport hesabında oturum açmadığınız sürece).
Harry Johnston

Yanıtlar:


130

System.DirectoryServices.AccountManagement kullanarak .NET 3.5'te C # .

 bool valid = false;
 using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
 {
     valid = context.ValidateCredentials( username, password );
 }

Bu, mevcut alan adına göre doğrulanacaktır. Diğer seçenekler için parametreli PrincipalContext yapıcısına göz atın.


@tvanfosson: DirectoryServices AD kullanmıyor mu?
Mitch Wheat

1
Evet. Ancak belgeler, bunun kimlik bilgilerini doğrulamanın hızlı bir yolu olduğunu gösteriyor. Ayrıca, nesneden herhangi bir özellik okumadığınız için soruda belirtilen bağlama yönteminden farklıdır. Yöntemin bir dizin nesnesi değil bağlamda olduğuna dikkat edin.
tvanfosson

Düzeltme: System.DirectoryServices.AccountManagement, .NET 3.5 gerektirir. ( msdn.microsoft.com/en-us/library/… )
Ian Boyd

19
Bunun new PrincipalContext(ContextType.Machine)yerine kullanırsanız yerel kullanıcılarla da çalışır .
VansFannel

Bunun önbelleğe alınmış kimlik bilgilerinde çalışıp çalışmadığını bilen var mı yoksa DC'ye bağlantı gerektiriyor mu? Şu anda üzerinde çalıştığım bazı uygulamalar için bunu bilmem gerekiyor ve şu anda test etmek için herhangi bir etki alanında değilim
Jcl

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

7
bu @ tvanfosson'ın 3 yıl önceki cevabında önemli bir fark içeriyor mu?
gbjbaanb

5
@gbjbaanb Evet, Domainoluştururken parametreyi içerdiğinden, PrincipalContextbilmek istediğim ve bu cevapta bulduğum bir şey.
Rudi Visser

1
@RudiVisser tvanfosson size "Diğer seçenekler için parametreleştirilmiş PrincipalContext yapıcısına bakın" önerisinde bulundu - her zaman dokümanları okuyun, hiçbir şey için asla internetin kelimesini kullanmayın! :)
gbjbaanb

4
D Basitçe bu söyleyerek: @gbjbaanb Evet tabii, ama StackOverflow mantra biz cevapları birden fazla başvuru kabul niçin en olduğunu, başka bir yerde okumak için bir çalışma örneği ziyade bir bağlantı ve öneri edilir sağlayan gelmez daha sağlarlar.
Rudi Visser

Bir UWP uygulamasında benzer bir şeyi nasıl yapabileceğimizi bilen var mı? (normal AD ile ve Azure AD ile değil). Burada bir soru sordum: stackoverflow.com/questions/42821447
slayernoah

7

Kimlik bilgilerini doğrulamak için aşağıdaki kodu kullanıyorum. Aşağıda gösterilen yöntem, kimlik bilgilerinin doğru olup olmadığını ve değilse parolanın süresinin dolup dolmadığını veya değiştirilmesi gerekip gerekmediğini onaylayacaktır.

Yıllardır böyle bir şey arıyordum ... Umarım bu birine yardımcı olur!

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

bu soruda açıklanan "yöntem 2" ... yani ... soruyu gerçekten yanıtlamıyor
Robert Levy

1

Yerel bir kullanıcının nasıl belirleneceği aşağıda açıklanmıştır:

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

Ian Boyd tarafından düzenleyin

Artık NTLM kullanmamalısınız. O kadar eski ve o kadar kötü ki Microsoft'un Uygulama Doğrulayıcısı (yaygın programlama hatalarını yakalamak için kullanılır) NTLM kullandığınızı algılarsa bir uyarı atar.

Birisi yanlışlıkla NTLM kullanıyorsa neden bir test yaptırdıklarına ilişkin Uygulama Doğrulayıcı belgelerinden bir bölüm:

NTLM Eklentisine Neden Gereklidir?

NTLM, uygulamaların ve işletim sisteminin güvenliğini potansiyel olarak tehlikeye atan kusurlara sahip, güncel olmayan bir kimlik doğrulama protokolüdür. En önemli eksiklik, bir saldırganın kullanıcıları sahte bir sunucuya bağlanmaları için kandırmasına olanak tanıyan sunucu kimlik doğrulamasının olmamasıdır. Eksik sunucu kimlik doğrulamasının bir sonucu olarak, NTLM kullanan uygulamalar, "yansıma" saldırısı olarak bilinen bir saldırı türüne karşı da savunmasız olabilir. Bu sonuncusu, bir saldırganın, bir kullanıcının kimlik doğrulama konuşmasını meşru bir sunucuya ele geçirmesine ve bunu, kullanıcının bilgisayarına saldırganın kimliğini doğrulamak için kullanmasına olanak tanır. NTLM'nin güvenlik açıkları ve bunları kullanma yolları, güvenlik topluluğundaki araştırma faaliyetlerini artırmanın hedefidir.

Kerberos yıllardır mevcut olmasına rağmen, birçok uygulama hala yalnızca NTLM'yi kullanmak üzere yazılmaktadır. Bu, uygulamaların güvenliğini gereksiz yere azaltır. Ancak Kerberos, tüm senaryolarda NTLM'nin yerini alamaz - esas olarak bir istemcinin bir etki alanına katılmamış sistemlerde kimlik doğrulaması yapması gereken durumlarda (belki de bunların en yaygın olanı ev ağıdır). Negotiate güvenlik paketi, mümkün olduğunda Kerberos kullanan ve başka bir seçenek olmadığında yalnızca NTLM'ye geri dönen geriye dönük uyumlu bir uzlaşmaya izin verir. Kodun NTLM yerine Anlaşma'yı kullanacak şekilde değiştirilmesi, müşterilerimiz için güvenliği önemli ölçüde artıracak ve uygulama uyumluluğunu çok az veya hiç sunmayacaktır. Tek başına pazarlık yapmak sihirli bir değnek değildir - bir saldırganın NTLM'ye düşürmeye zorlayabileceği durumlar vardır, ancak bu durumlardan yararlanılması çok daha zordur. Ancak, hemen bir gelişme, Negotiate'i doğru kullanmak için yazılan uygulamaların otomatik olarak NTLM yansıtma saldırılarına karşı bağışık olmasıdır.

NTLM kullanımına karşı son bir uyarı olarak: Windows'un gelecekteki sürümlerinde işletim sisteminde NTLM kullanımını devre dışı bırakmak mümkün olacaktır. Uygulamaların NTLM'ye sıkı bir bağımlılığı varsa, NTLM devre dışı bırakıldığında kimlik doğrulaması yapamazlar.

Eklenti Nasıl Çalışır?

Doğrulayıcı fişi aşağıdaki hataları algılar:

  • NTLM paketi, doğrudan AcquireCredentialsHandle (veya daha yüksek düzey sarıcı API) çağrısında belirtilir.

  • InitializeSecurityContext çağrısındaki hedef adı NULL.

  • InitializeSecurityContext çağrısındaki hedef adı, düzgün biçimlendirilmiş bir SPN, UPN veya NetBIOS tarzı etki alanı adı değil.

Son iki durum, Negotiate'i doğrudan (ilk durum) veya dolaylı olarak NTLM'ye geri dönmeye zorlar (etki alanı denetleyicisi, ikinci durumda "ana öğe bulunamadı" hatası döndürerek Anlaşma'nın geri dönmesine neden olur).

Eklenti ayrıca NTLM'ye sürüm düşürme algıladığında da uyarıları günlüğe kaydeder; örneğin, Etki Alanı Denetleyicisi tarafından bir SPN bulunamadığında. Bunlar, genellikle meşru vakalar olduklarından - örneğin, etki alanına katılmamış bir sistemde kimlik doğrulaması yaparken, yalnızca uyarı olarak günlüğe kaydedilir.

NTLM Durur

5000 - Uygulama Açıkça NTLM Paketini Seçti

Önem - Hata

Uygulama veya alt sistem, AcquireCredentialsHandle çağrısında Anlaşma yerine açıkça NTLM'yi seçer. İstemci ve sunucunun Kerberos kullanarak kimlik doğrulaması mümkün olsa da bu, NTLM'nin açık seçilmesi ile engellenir.

Bu Hatayı Nasıl Düzeltebilirim?

Bu hatanın düzeltmesi, NTLM yerine Anlaşma paketini seçmektir. Bunun nasıl yapılacağı, istemci veya sunucu tarafından kullanılan belirli Ağ alt sistemine bağlı olacaktır. Bazı örnekler aşağıda verilmiştir. Kullandığınız belirli kitaplık veya API kümesiyle ilgili belgelere başvurmalısınız.

APIs(parameter) Used by Application    Incorrect Value  Correct Value  
=====================================  ===============  ========================
AcquireCredentialsHandle (pszPackage)  “NTLM”           NEGOSSP_NAME “Negotiate”

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


System.DirectoryServices.AccountManagement ad alanı .NET
Jeremy Gray

1
Bunun yaklaşık 4 yaşında olduğunu biliyorum, ancak yerel bir kullanıcıyı doğruluyorsanız, bir PrincipalContext oluştururken ContextType'ı ContextType.Machine olarak ayarladığınızdan emin olmanız gerekir. Aksi takdirde Domain değişkeninde verilen makine adının aslında bir domain sunucusu olduğunu düşünecektir.
SolidRegardless
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.