76

Tôi muốn xác thực một bộ thông tin xác thực đối với bộ điều khiển miền. ví dụ:

Username: STACKOVERFLOW\joel
Password: splotchy

Phương pháp 1. Truy vấn Active Directory với Mạo danh

Rất nhiều người đề nghị truy vấn Active Directory cho một cái gì đó. Nếu một ngoại lệ được đưa ra, thì bạn biết thông tin đăng nhập không hợp lệ - như được đề xuất trong câu hỏi stackoverflow này .

Tuy nhiên, có một số nhược điểm nghiêm trọng đối với phương pháp này :

  1. Bạn không chỉ xác thực tài khoản miền mà còn thực hiện kiểm tra ủy quyền ngầm. Đó là, bạn đang đọc các thuộc tính từ AD bằng cách sử dụng mã thông báo mạo danh. Điều gì xảy ra nếu tài khoản hợp lệ khác không có quyền đọc từ AD? Theo mặc định, tất cả người dùng đều có quyền truy cập đọc, nhưng chính sách miền có thể được đặt để tắt quyền truy cập cho các tài khoản bị hạn chế (và hoặc nhóm).

  2. Liên kết với AD có chi phí nghiêm trọng, bộ đệm lược đồ AD phải được tải tại máy khách (bộ đệm ADSI trong nhà cung cấp ADSI được sử dụng bởi DirectoryService). Đây là cả mạng và máy chủ AD, tiêu tốn tài nguyên - và quá tốn kém cho một thao tác đơn giản như xác thực tài khoản người dùng.

  3. Bạn đang dựa vào một lỗi ngoại lệ cho một trường hợp không đặc biệt và giả sử điều đó có nghĩa là tên người dùng và mật khẩu không hợp lệ. Các vấn đề khác (ví dụ như lỗi mạng, lỗi kết nối AD, lỗi cấp phát bộ nhớ, v.v.) sau đó được hiểu sai là lỗi xác thực.

Phương pháp 2. LogonUser Win32 API

Những người khác đã đề xuất sử dụng LogonUser()chức năng API. Điều này nghe có vẻ hay, nhưng thật không may, người dùng gọi đôi khi cần một sự cho phép thường chỉ được cấp cho chính hệ điều hành:

Quá trình gọi LogonUser yêu cầu đặc quyền SE_TCB_NAME. Nếu quá trình gọi không có đặc quyền này, LogonUser sẽ thất bại và GetLastError trả về ERROR_PRIVILEGE_NOT_HELD.

Trong một số trường hợp, quy trình gọi LogonUser cũng phải được bật đặc quyền SE_CHANGE_NOTIFY_NAME; mặt khác, LogonUser không thành công và GetLastError trả về ERROR_ACCESS_DENIED. Đặc quyền này không bắt buộc đối với tài khoản hệ thống cục bộ hoặc tài khoản là thành viên của nhóm quản trị viên. Theo mặc định, SE_CHANGE_NOTIFY_NAME được bật cho tất cả người dùng, nhưng một số quản trị viên có thể vô hiệu hóa nó cho mọi người.

Trao đặc quyền " Hoạt động như một phần của hệ điều hành " không phải là điều bạn muốn làm willy-nilly - như Microsoft đã chỉ ra trong một bài viết cơ sở tri thức :

... Quá trình đang gọi LogonUser phải có đặc quyền SE_TCB_NAME (trong Trình quản lý người dùng, đây là quyền " Hoạt động như một phần của Hệ điều hành "). Đặc quyền SE_TCB_NAME rất mạnh mẽ và không nên được cấp cho bất kỳ người dùng tùy ý nào để họ có thể chạy một ứng dụng cần xác thực thông tin đăng nhập.

Ngoài ra, một cuộc gọi đến LogonUser()sẽ thất bại nếu mật khẩu trống được chỉ định.


Cách thích hợp để xác thực một bộ thông tin xác thực tên miền là gì?


Tôi tình cờ gọi từ mã được quản lý, nhưng đây là một câu hỏi chung của Windows. Có thể giả định rằng các khách hàng đã cài đặt .NET Framework 2.0.

|
  • 1

    Bạn đọc cần lưu ý rằng kể từ Windows XP, LogonUser không còn yêu cầu SE_TCB_NAME (trừ khi bạn đang đăng nhập vào tài khoản Passport).

    – Tạ Khánh Vy 05:42:49 06/08/2014
117

C # trong .NET 3.5 bằng System.DirectoryService.AccountQuản lý .

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

Điều này sẽ xác nhận đối với tên miền hiện tại. Kiểm tra hàm tạo của ProtalContext được tham số hóa cho các tùy chọn khác.

|
  • 1

    @tvanfosson: DirectoryService không sử dụng AD?

    – Phan Thúy Nga 02:45:27 29/11/2008
  • 1

    Vâng. Nhưng tài liệu chỉ ra rằng đây là một cách nhanh chóng để xác nhận thông tin đăng nhập. Nó cũng khác với phương thức ràng buộc được đề cập trong câu hỏi vì bạn không đọc bất kỳ thuộc tính nào từ đối tượng. Lưu ý rằng phương thức nằm trong ngữ cảnh, không phải là một đối tượng thư mục.

    – Ngô Phương Lan 02:53:18 29/11/2008
  • 1

    Sửa lỗi: System.DirectoryService.AccountQuản lý yêu cầu .NET 3.5. ( msdn.microsoft.com/en-us/l Library / Google )

    – Tạ Tuyết Oanh 14:51:39 29/11/2008
  • 1

    Nó cũng hoạt động với người dùng cục bộ nếu bạn sử dụng new PrincipalContext(ContextType.Machine)thay thế.

    – Hồ Mỹ Tâm 10:18:33 06/05/2014
  • 1

    Có ai biết nếu điều này hoạt động trên các thông tin lưu trữ, hoặc nó yêu cầu kết nối với DC? Tôi cần biết điều này đối với một số triển khai Tôi hiện đang làm việc và hiện tại tôi không có bất kỳ tên miền nào để kiểm tra

    – Dương Nam Hưng 17:52:45 12/05/2016
17
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);
        }
    }
}
|
  • 1

    điều này có chứa bất kỳ sự khác biệt đáng kể nào đối với câu trả lời của @ tvanfosson 3 năm trước không?

    – Phan Thúy Nga 09:01:23 29/11/2013
  • 1

    @gbjbaanb Vâng, vì nó chứa Domaintham số khi tạo PrincipalContext, một cái gì đó mà tôi quan tâm muốn biết và tìm thấy trong câu trả lời này.

    – Ngô Phương Lan 13:50:19 14/01/2015
  • 1

    @RudiVisser tvanfosson đã gợi ý cho bạn "Kiểm tra hàm tạo của ProtalContext được tham số hóa cho các tùy chọn khác" - luôn đọc tài liệu, không bao giờ lấy từ internet cho bất cứ điều gì! :)

    – Tạ Tuyết Oanh 14:45:26 14/01/2015
  • 1

    @gbjbaanb Có tất nhiên, nhưng cung cấp một ví dụ làm việc chứ không phải là một liên kết và gợi ý để đọc ở đâu đó là câu thần chú StackOverflow, đó là lý do tại sao chúng tôi chấp nhận nhiều lần gửi câu trả lời: D Đơn giản chỉ cần nói rằng đây không cung cấp thêm.

    – Hồ Mỹ Tâm 14:47:00 14/01/2015
  • 1

    Có ai biết làm thế nào chúng ta có thể làm điều gì đó tương tự trong một ứng dụng UWP không? (với AD thông thường và không phải với Azure AD). Tôi đã hỏi một câu hỏi ở đây: stackoverflow.com/questions/42821447

    – Dương Nam Hưng 20:19:43 21/03/2017
5

Tôi đang sử dụng đoạn mã sau để xác thực thông tin đăng nhập. Phương pháp hiển thị dưới đây sẽ xác nhận nếu thông tin đăng nhập là chính xác và nếu không, mật khẩu đã hết hạn hoặc cần thay đổi.

Tôi đã tìm kiếm thứ gì đó như thế này từ lâu ... Vì vậy, tôi hy vọng điều này sẽ giúp được ai đó!

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

|
  • 1

    Không gian tên System.DirectoryService.AccountQuản lý là mới trong .NET 3.5

    – Tạ Uy Phong 16:49:23 09/08/2011
  • 1

    Tôi biết điều này đã gần 4 tuổi, nhưng nếu bạn đang xác thực người dùng cục bộ, bạn sẽ cần đảm bảo rằng bạn đặt ContextType thành ContextType.Machine khi bạn xây dựng một ProtalContext. Nếu không, nó sẽ nghĩ tên máy được cung cấp trong biến Miền thực sự là một máy chủ miền.

    – Phan Thùy Oanh 13:36:33 13/12/2013
1

Đây là cách xác định người dùng cục bộ:

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

Chỉnh sửa bởi Ian Boyd

Bạn không nên sử dụng NTLM nữa. Nó quá cũ và tệ đến mức Trình xác minh ứng dụng của Microsoft (được sử dụng để bắt các lỗi lập trình phổ biến) sẽ đưa ra cảnh báo nếu phát hiện bạn sử dụng NTLM.

Đây là một chương trong tài liệu Trình xác minh ứng dụng về lý do tại sao họ có bài kiểm tra nếu ai đó sử dụng nhầm NTLM:

Tại sao cần bổ trợ NTLM

NTLM là một giao thức xác thực lỗi thời với các lỗ hổng có khả năng ảnh hưởng đến bảo mật của các ứng dụng và hệ điều hành. Thiếu sót quan trọng nhất là thiếu xác thực máy chủ, điều này có thể cho phép kẻ tấn công lừa người dùng kết nối với máy chủ giả mạo. Là một hệ quả của việc xác thực máy chủ bị thiếu, các ứng dụng sử dụng NTLM cũng có thể dễ bị tấn công bởi một loại tấn công được gọi là tấn công phản xạ Hồi giáo. Điều này sau đó cho phép kẻ tấn công chiếm quyền điều khiển cuộc trò chuyện xác thực của người dùng đến một máy chủ hợp pháp và sử dụng nó để xác thực kẻ tấn công vào máy tính của người dùng. Các lỗ hổng và cách khai thác chúng của NTLM là mục tiêu của việc tăng cường hoạt động nghiên cứu trong cộng đồng bảo mật.

Mặc dù Kerberos đã có sẵn trong nhiều năm, nhiều ứng dụng vẫn được viết để chỉ sử dụng NTLM. Điều này không cần thiết làm giảm tính bảo mật của các ứng dụng. Tuy nhiên, Kerberos không thể thay thế NTLM trong tất cả các kịch bản - chủ yếu là những nơi khách hàng cần xác thực với các hệ thống không được kết nối với một miền (mạng gia đình có lẽ là phổ biến nhất trong số này). Gói bảo mật đàm phán cho phép thỏa hiệp tương thích ngược sử dụng Kerberos bất cứ khi nào có thể và chỉ hoàn nguyên về NTLM khi không có tùy chọn nào khác. Chuyển đổi mã để sử dụng Đàm phán thay vì NTLM sẽ tăng đáng kể bảo mật cho khách hàng của chúng tôi trong khi giới thiệu một vài hoặc không có sự tương thích ứng dụng. Đàm phán tự nó không phải là một viên đạn bạc - có những trường hợp kẻ tấn công có thể buộc hạ cấp xuống NTLM nhưng những điều này khó khai thác hơn đáng kể. Tuy nhiên, một cải tiến ngay lập tức là các ứng dụng được viết để sử dụng đàm phán chính xác sẽ tự động miễn nhiễm với các cuộc tấn công phản chiếu NTLM.

Bằng cách thận trọng cuối cùng đối với việc sử dụng NTLM: trong các phiên bản Windows trong tương lai, có thể vô hiệu hóa việc sử dụng NTLM tại hệ điều hành. Nếu các ứng dụng có sự phụ thuộc lớn vào NTLM, đơn giản là chúng sẽ không xác thực được khi NTLM bị tắt.

Cách thức hoạt động của Plug-in

Trình cắm Verifier phát hiện các lỗi sau:

  • Gói NTLM được chỉ định trực tiếp trong lệnh gọi tới AcquireCredentialsHandle (hoặc API trình bao bọc cấp cao hơn).

  • Tên mục tiêu trong lệnh gọi LaunchizeSecurityContext là NULL.

  • Tên mục tiêu trong lệnh gọi LaunchizeSecurityContext không phải là tên miền kiểu SPN, UPN hoặc NetBIOS được tạo đúng.

Hai trường hợp sau sẽ buộc đàm phán quay trở lại NTLM trực tiếp (trường hợp đầu tiên) hoặc gián tiếp (bộ điều khiển miền sẽ trả về một hiệu trưởng không được tìm thấy lỗi trong trường hợp thứ hai khiến cho đàm phán rơi trở lại).

Trình cắm cũng ghi lại các cảnh báo khi phát hiện hạ cấp xuống NTLM; ví dụ: khi Bộ điều khiển miền không tìm thấy SPN. Chúng chỉ được ghi lại dưới dạng cảnh báo vì chúng thường là các trường hợp hợp pháp - ví dụ: khi xác thực hệ thống không tham gia miền.

Dừng NTLM

5000 - Ứng dụng đã chọn gói NTLM rõ ràng

Mức độ nghiêm trọng - Lỗi

Ứng dụng hoặc hệ thống con rõ ràng chọn NTLM thay vì đàm phán trong lệnh gọi tới AcquireCredentialsHandle. Mặc dù máy khách và máy chủ có thể xác thực bằng Kerberos nhưng điều này bị ngăn chặn bởi sự lựa chọn rõ ràng của NTLM.

Cách khắc phục lỗi này

Cách khắc phục lỗi này là chọn gói Đàm phán thay cho NTLM. Làm thế nào điều này được thực hiện sẽ phụ thuộc vào hệ thống con Mạng cụ thể đang được sử dụng bởi máy khách hoặc máy chủ. Một số ví dụ được đưa ra dưới đây. Bạn nên tham khảo tài liệu về thư viện hoặc bộ API cụ thể mà bạn đang sử dụng.

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

Câu trả lời của bạn (> 20 ký tự)

Bằng cách click "Đăng trả lời", bạn đồng ý với Điều khoản dịch vụ, Chính sách bảo mật and Chính sách cookie của chúng tôi.

Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ hoặc hỏi câu hỏi của bạn.