डोमेन क्रेडेंशियल्स को कैसे मान्य करें?


86

मैं डोमेन नियंत्रक के खिलाफ क्रेडेंशियल्स के एक सेट को मान्य करना चाहता हूं। उदाहरण के लिए:

Username: STACKOVERFLOW\joel
Password: splotchy

विधि 1. प्रतिरूपण के साथ क्वेरी सक्रिय निर्देशिका

बहुत से लोग कुछ के लिए सक्रिय निर्देशिका को क्वेरी करने का सुझाव देते हैं। यदि एक अपवाद को फेंक दिया जाता है, तो आप जानते हैं कि क्रेडेंशियल्स मान्य नहीं हैं - जैसा कि इस स्टैकओवरफ़्लो प्रश्न में सुझाया गया है ।

हालाँकि इस दृष्टिकोण में कुछ गंभीर कमियाँ हैं :

  1. आप न केवल एक डोमेन खाते को प्रमाणित कर रहे हैं, बल्कि एक अंतर्निहित प्राधिकरण जांच भी कर रहे हैं। यही है, आप एक प्रतिरूपण टोकन का उपयोग करके विज्ञापन से गुण पढ़ रहे हैं। क्या होगा यदि अन्यथा मान्य खाते में AD से पढ़ने का कोई अधिकार नहीं है? डिफ़ॉल्ट रूप से सभी उपयोगकर्ताओं ने पहुंच पढ़ ली है, लेकिन प्रतिबंधित नीतियों (और या समूहों) तक पहुंच की अनुमति को अक्षम करने के लिए डोमेन नीतियां निर्धारित की जा सकती हैं।

  2. AD के खिलाफ बाइंडिंग एक गंभीर ओवरहेड है, AD स्कीमा कैश को क्लाइंट (ADSI कैश इन डायरेक्ट्री सर्विसेज द्वारा उपयोग किए जाने वाले ADSI कैश) पर लोड किया जाना है। यह नेटवर्क, और AD सर्वर, संसाधन खपत दोनों है - और उपयोगकर्ता खाते को प्रमाणित करने जैसे सरल ऑपरेशन के लिए बहुत महंगा है।

  3. आप एक गैर-असाधारण मामले के लिए एक अपवाद विफलता पर भरोसा कर रहे हैं, और यह मानकर कि अमान्य उपयोगकर्ता नाम और पासवर्ड है। अन्य समस्याएं (उदाहरण के लिए नेटवर्क विफलता, AD कनेक्टिविटी विफलता, मेमोरी आवंटन त्रुटि, आदि) को फिर प्रमाणीकरण विफलता के रूप में गलत रूप से समझा जाता है।

विधि 2. LogonUser Win32 एपीआई

अन्य लोगों ने LogonUser()एपीआई फ़ंक्शन का उपयोग करने का सुझाव दिया है । यह अच्छा लगता है, लेकिन दुर्भाग्य से कॉलिंग उपयोगकर्ता को कभी-कभी केवल ऑपरेटिंग सिस्टम को दी गई अनुमति की आवश्यकता होती है:

LogonUser को कॉल करने वाली प्रक्रिया को SE_TCB_NAME विशेषाधिकार की आवश्यकता होती है। यदि कॉलिंग प्रक्रिया में यह विशेषाधिकार नहीं है, तो LogonUser विफल हो जाता है और GetLastError ERROR_PRIVILEGE_NOT_HELD लौटा देता है।

कुछ मामलों में, लॉगऑनसर को कॉल करने वाली प्रक्रिया में SE_CHANGE_NOTIFY_NAME विशेषाधिकार भी होना चाहिए; अन्यथा, LogonUser विफल हो जाता है और GetLastError ERROR_ACCESS_DENIED देता है। यह विशेषाधिकार स्थानीय सिस्टम खाते या खातों के लिए आवश्यक नहीं है जो व्यवस्थापक समूह के सदस्य हैं। डिफ़ॉल्ट रूप से, SE_CHANGE_NOTIFY_NAME सभी उपयोगकर्ताओं के लिए सक्षम है, लेकिन कुछ व्यवस्थापक इसे सभी के लिए अक्षम कर सकते हैं।

" अधिनियम ऑपरेटिंग सिस्टम के एक भाग के रूप में अधिनियम " को सौंपना विशेषाधिकार कुछ ऐसा नहीं है जिसे आप विली-निली करना चाहते हैं - जैसा कि Microsoft ज्ञानकोष लेख में बताते हैं :

... लॉगऑनसर को कॉल करने वाली प्रक्रिया में SE_TCB_NAME विशेषाधिकार होना चाहिए (उपयोगकर्ता प्रबंधक में, यह " ऑपरेटिंग सिस्टम के भाग के रूप में अधिनियम " सही है)। SE_TCB_NAME विशेषाधिकार बहुत शक्तिशाली है और इसे किसी भी मनमाने उपयोगकर्ता को नहीं दिया जाना चाहिए ताकि वे एक ऐसा एप्लिकेशन चला सकें जिसे क्रेडेंशियल्स को मान्य करने की आवश्यकता हो।

इसके अतिरिक्त, LogonUser()खाली पासवर्ड निर्दिष्ट किए जाने पर कॉल विफल हो जाएगी।


डोमेन क्रेडेंशियल्स के एक सेट को प्रमाणित करने का उचित तरीका क्या है?


मैं ऐसा प्रबंधित कोड से कॉल कर रहे हैं, लेकिन यह आ सामान्य विंडोज सवाल है। यह माना जा सकता है कि ग्राहकों के पास .NET फ्रेमवर्क 2.0 स्थापित है।


1
पाठकों को ध्यान देना चाहिए कि Windows XP के रूप में, LogonUser को अब SE_TCB_NAME की आवश्यकता नहीं है (जब तक कि आप पासपोर्ट खाते में प्रवेश नहीं कर रहे हैं)।
हैरी जॉनसन

जवाबों:


130

System.DirectoryServices.AccountManagement का उपयोग करके C # .NET 3.5 में ।

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

यह वर्तमान डोमेन के विरुद्ध मान्य होगा। अन्य विकल्पों के लिए पैरामीटर किए गए प्रिंसिपल कॉन्टेक्स्ट कंस्ट्रक्टर को देखें।


@tvanfosson: DirectoryServices AD का उपयोग नहीं करती है?
मिच गेहूं

1
हाँ। लेकिन प्रलेखन इंगित करता है कि यह क्रेडेंशियल्स को मान्य करने का एक तेज़ तरीका है। यह प्रश्न में उल्लिखित बाध्यकारी विधि से अलग है क्योंकि आप ऑब्जेक्ट से किसी भी गुण को नहीं पढ़ रहे हैं। ध्यान दें कि विधि संदर्भ पर है, निर्देशिका ऑब्जेक्ट नहीं है।
tvanfosson

सुधार: System.DirectoryServices.AccountManagement .NET 3.5 की आवश्यकता है। ( msdn.microsoft.com/en-us/library/… )
इयान बॉयड

19
यदि आप new PrincipalContext(ContextType.Machine)इसके बजाय इसका उपयोग करते हैं तो यह स्थानीय उपयोगकर्ताओं के साथ भी काम करता है ।
VansFannel

क्या किसी को पता है कि यह कैश्ड क्रेडेंशियल्स पर काम करता है, या क्या इसे डीसी से कनेक्शन की आवश्यकता है? मुझे कुछ कार्यान्वयन के लिए यह जानने की आवश्यकता है जो मैं अभी काम कर रहा हूं और मैं वर्तमान में परीक्षण करने के लिए किसी भी डोमेन पर नहीं हूं
जेसीएल

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
क्या 3 साल पहले @ tvanfosson के उत्तर में कोई महत्वपूर्ण अंतर है?
gbjbaanb

5
@ GBjbaanb हाँ, क्योंकि इसमें Domainपैरामीटर है जब इसे बनाते हैं PrincipalContext, तो ऐसा कुछ जिसे मैं जानने के इच्छुक था और इस उत्तर में पाया गया था।
रूडी विसेर

1
@RudiVisser tvanfosson ने आपको "अन्य विकल्पों के लिए पैरामीटर किए गए प्रिंसिपल कॉन्टेक्स्ट कंस्ट्रक्टर की जाँच करें" का सुझाव दिया है - हमेशा डॉक्स पढ़ें, कभी भी किसी भी चीज़ के लिए इंटरनेट का शब्द न लें! :)
gbjbaanb

4
@ जीबीजैनब हां, लेकिन एक लिंक और सुझाव के बजाय एक काम का उदाहरण प्रदान करना कहीं और पढ़ने का सुझाव है, स्टैकऑवरफ्लो मंत्र है, इसीलिए हम कई जवाबों को स्वीकार करते हैं: डी बस यह कहते हुए कि यह अधिक प्रदान करता है
रूडी विसेर

क्या किसी को पता है कि हम UWP ऐप में कुछ ऐसा कैसे कर सकते हैं? (नियमित AD के साथ और Azure AD के साथ नहीं)। मैंने यहाँ एक प्रश्न पूछा है: stackoverflow.com/questions/42821447
slayernoah

7

मैं क्रेडेंशियल्स को मान्य करने के लिए निम्न कोड का उपयोग कर रहा हूं। नीचे दी गई विधि यह पुष्टि करेगी कि क्या क्रेडेंशियल्स सही हैं और यदि पासवर्ड नहीं हटाए गए हैं या परिवर्तन की आवश्यकता है।

मैं उम्र के लिए कुछ इस तरह की तलाश में था ... तो मुझे आशा है कि यह किसी की मदद करता है!

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

यह प्रश्न में वर्णित "विधि 2" है ... इसलिए ... वास्तव में इस सवाल का जवाब नहीं
रॉबर्ट लेवी

1

स्थानीय उपयोगकर्ता का निर्धारण कैसे करें:

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

इयान बॉयड द्वारा संपादित

अब आपको NTLM का उपयोग बिल्कुल नहीं करना चाहिए। यह इतना पुराना है, और इतना बुरा है, कि Microsoft का एप्लिकेशन वेरिफायर (जो सामान्य प्रोग्रामिंग गलतियों को पकड़ने के लिए उपयोग किया जाता है) एक चेतावनी को फेंक देगा यदि यह NTLM का उपयोग करके आपको पता लगाता है।

यहां एप्लिकेशन वेरीफ़ायर डॉक्यूमेंटेशन के एक अध्याय के बारे में बताया गया है कि अगर किसी के पास NTLM का उपयोग करने की गलती है, तो उनका परीक्षण क्यों है:

NTLM प्लग-इन की आवश्यकता क्यों है

एनटीएलएम खामियों के साथ एक पुराना प्रमाणीकरण प्रोटोकॉल है जो संभावित रूप से अनुप्रयोगों और ऑपरेटिंग सिस्टम की सुरक्षा से समझौता करता है। सबसे महत्वपूर्ण कमी सर्वर प्रमाणीकरण की कमी है, जो एक हमलावर को उपयोगकर्ताओं को एक स्पूफ सर्वर से कनेक्ट करने की अनुमति दे सकती है। अनुपलब्ध सर्वर प्रमाणीकरण के एक कोरोलरी के रूप में, एनटीएलएम का उपयोग करने वाले एप्लिकेशन एक प्रकार के हमले के लिए "प्रतिबिंब" हमले के रूप में भी असुरक्षित हो सकते हैं। यह बाद में किसी हमलावर को किसी वैध सर्वर पर उपयोगकर्ता की प्रमाणीकरण बातचीत को हाईजैक करने की अनुमति देता है और इसका उपयोग उपयोगकर्ता के कंप्यूटर पर हमलावर को प्रमाणित करने के लिए करता है। एनटीएलएम की कमजोरियां और उनका शोषण करने के तरीके सुरक्षा समुदाय में अनुसंधान गतिविधि को बढ़ाने का लक्ष्य हैं।

हालांकि केर्बरोस कई वर्षों से उपलब्ध है, फिर भी कई एप्लिकेशन अभी भी केवल NTLM का उपयोग करने के लिए लिखे गए हैं। यह अनावश्यक रूप से अनुप्रयोगों की सुरक्षा को कम करता है। हालांकि केर्बोस NTLM को सभी परिदृश्यों में प्रतिस्थापित नहीं कर सकता है - मुख्यतः वे जहां एक क्लाइंट को उन प्रणालियों को प्रमाणित करने की आवश्यकता होती है जो एक डोमेन में शामिल नहीं होती हैं (एक होम नेटवर्क शायद इनमें से सबसे आम है)। नेगोशिएट सिक्योरिटी पैकेज एक बैकवर्ड-कम्पेटिबल कॉम्प्रोमाइज़ की अनुमति देता है जो जब भी संभव हो, केर्बेरोस का उपयोग करता है और केवल NTLM के लिए कोई अन्य विकल्प न होने पर पलट देता है। NTLM के बजाय Negotiate का उपयोग करने के लिए कोड स्विच करना कुछ या कोई एप्लिकेशन संगतता प्रस्तुत करते समय हमारे ग्राहकों के लिए सुरक्षा बढ़ा देगा। अपने आप में बातचीत एक चांदी की गोली नहीं है - ऐसे मामले हैं जहां एक हमलावर NTLM को अपग्रेड करने के लिए मजबूर कर सकता है लेकिन ये शोषण करने के लिए काफी अधिक कठिन हैं। हालांकि, एक तात्कालिक सुधार यह है कि नेगोशिएट का सही ढंग से उपयोग करने के लिए लिखे गए एप्लिकेशन एनटीएलएम प्रतिबिंब हमलों के लिए स्वचालित रूप से प्रतिरक्षा हैं।

NTLM के उपयोग के खिलाफ सावधानी के अंतिम शब्द के माध्यम से: विंडोज के भविष्य के संस्करणों में ऑपरेटिंग सिस्टम पर NTLM के उपयोग को अक्षम करना संभव होगा। यदि अनुप्रयोगों का NTLM पर एक कठिन निर्भरता है, तो वे केवल NTLM के अक्षम होने पर प्रमाणित करने में विफल रहेंगे।

कैसे काम करता है प्लग-इन

सत्यापनकर्ता प्लग निम्न त्रुटियों का पता लगाता है:

  • NTLM पैकेज को सीधे AcquireCredentialsHandle (या उच्च स्तरीय आवरण API) के लिए कॉल में निर्दिष्ट किया गया है।

  • कॉल में InitializeSecurityContext का लक्ष्य नाम NULL है।

  • InitializeSecurityContext के लिए कॉल में लक्षित नाम ठीक से गठित SPN, UPN या NetBIOS- शैली डोमेन नाम नहीं है।

बाद के दो मामले नेगोशिएट को एनटीएलएम को सीधे (पहले मामले) या अप्रत्यक्ष रूप से वापस करने के लिए मजबूर करेंगे (डोमेन नियंत्रक दूसरे मामले में "प्रिंसिपल नहीं मिला" त्रुटि लौटाएगा जिससे नेगोशिएट वापस आ जाएगी)।

प्लग-इन चेतावनी भी लॉग करता है जब यह एनटीएलएम को डाउनग्रेड करता है; उदाहरण के लिए, जब डोमेन नियंत्रक द्वारा कोई SPN नहीं मिलता है। इन्हें केवल चेतावनी के रूप में लॉग किया जाता है क्योंकि वे अक्सर वैध मामले होते हैं - उदाहरण के लिए, जब एक सिस्टम को प्रमाणित करना जो कि डोमेन में शामिल नहीं है।

NTLM रुक जाता है

5000 - आवेदन स्पष्ट रूप से चयनित एनटीएलएम पैकेज है

गंभीरता - त्रुटि

एप्लिकेशन या सबसिस्टम स्पष्ट रूप से AcquireCredentialsHandle के लिए कॉल में बातचीत के बजाय NTLM का चयन करता है। भले ही यह क्लाइंट और सर्वर के लिए करबरोस का उपयोग करके प्रमाणित करने के लिए संभव हो सकता है लेकिन एनटीएलएम के स्पष्ट चयन से इसे रोका जाता है।

इस त्रुटि को कैसे ठीक करें

इस त्रुटि के लिए NTLM के स्थान पर Negotiate पैकेज का चयन करना है। यह कैसे किया जाता है यह ग्राहक या सर्वर द्वारा उपयोग किए जा रहे विशेष नेटवर्क सबसिस्टम पर निर्भर करेगा। कुछ उदाहरण नीचे दिए गए हैं। आपको विशेष पुस्तकालय या एपीआई सेट पर प्रलेखन से परामर्श करना चाहिए जो आप उपयोग कर रहे हैं।

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

काशिफ मुश्ताक ओटावा, कनाडा


System.DirectoryServices.AccountManagement नामस्थान .NET 3.5 में नया था
जेरेमी ग्रे

1
मुझे पता है कि यह लगभग 4 साल पुराना है, लेकिन यदि आप एक स्थानीय उपयोगकर्ता को मान्य कर रहे हैं, तो आपको यह सुनिश्चित करना होगा कि आप एक प्रिंसिपल कॉन्टेक्स्ट का निर्माण करते समय संदर्भ को ContextType.Machine पर सेट करें। अन्यथा यह सोचेंगे कि डोमेन चर में प्रदान की गई मशीन का नाम वास्तव में एक डोमेन सर्वर है।
सॉलिडग्रैजलेस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.