Làm thế nào để bạn làm mạo danh trong .NET?


139

Có cách nào đơn giản để mạo danh người dùng trong .NET không?

Cho đến nay tôi đã sử dụng lớp này từ dự án mã cho tất cả các yêu cầu mạo danh của tôi.

Có cách nào tốt hơn để làm điều đó bằng cách sử dụng .NET Framework không?

Tôi có một bộ thông tin người dùng, (tên người dùng, mật khẩu, tên miền) đại diện cho danh tính tôi cần để mạo danh.


1
Bạn có thể đặc sắc hơn không? Có rất nhiều cách để mạo danh ra khỏi hộp.
Esteban Araya

Câu trả lời:


60

Dưới đây là một số tổng quan tốt về các khái niệm mạo danh .NET.

Về cơ bản, bạn sẽ tận dụng các lớp không có trong khung .NET:

Mã thường có thể dài dòng và đó là lý do tại sao bạn thấy nhiều ví dụ như ví dụ bạn tham khảo cố gắng đơn giản hóa quy trình.


4
Chỉ cần lưu ý rằng mạo danh không phải là viên đạn bạc và một số API đơn giản là không được thiết kế để hoạt động với mạo danh.
Lex Li

Liên kết đó từ blog của lập trình viên người Hà Lan là tuyệt vời. Cách tiếp cận trực quan hơn nhiều để mạo danh so với các kỹ thuật khác được trình bày.
code4life

296

"Mạo danh" trong không gian .NET thường có nghĩa là chạy mã trong một tài khoản người dùng cụ thể. Đây là một khái niệm hơi riêng biệt so với việc truy cập vào tài khoản người dùng đó thông qua tên người dùng và mật khẩu, mặc dù hai ý tưởng này kết hợp với nhau thường xuyên. Tôi sẽ mô tả cả hai, và sau đó giải thích cách sử dụng thư viện SimpleImpersonation của tôi , sử dụng chúng trong nội bộ.

mạo danh

Các API để mạo danh được cung cấp trong .NET thông qua System.Security.Principalkhông gian tên:

  • Mã mới hơn (.NET 4.6+, .NET Core, v.v.) thường nên sử dụng WindowsIdentity.RunImpersonated, chấp nhận xử lý mã thông báo của tài khoản người dùng và sau đó Actionhoặc Func<T>để mã thực thi.

    WindowsIdentity.RunImpersonated(tokenHandle, () =>
    {
        // do whatever you want as this user.
    });
    

    hoặc là

    var result = WindowsIdentity.RunImpersonated(tokenHandle, () =>
    {
        // do whatever you want as this user.
        return result;
    });
    
  • Mã cũ hơn đã sử dụng WindowsIdentity.Impersonatephương thức để lấy một WindowsImpersonationContextđối tượng. Đối tượng này thực hiện IDisposable, do đó thường được gọi từ một usingkhối.

    using (WindowsImpersonationContext context = WindowsIdentity.Impersonate(tokenHandle))
    {
        // do whatever you want as this user.
    }
    

    Mặc dù API này vẫn tồn tại trong .NET Framework, nhưng nói chung nên tránh và không có sẵn trong .NET Core hoặc .NET Standard.

Truy cập tài khoản người dùng

API để sử dụng tên người dùng và mật khẩu để có quyền truy cập vào tài khoản người dùng trong Windows là LogonUser- đó là API gốc Win32. Hiện tại không có API .NET tích hợp để gọi nó, vì vậy người ta phải dùng đến P / Invoke.

[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);

Đây là định nghĩa cuộc gọi cơ bản, tuy nhiên còn nhiều điều cần xem xét để thực sự sử dụng nó trong sản xuất:

  • Có được một tay cầm với mẫu truy cập "an toàn".
  • Đóng tay cầm bản địa một cách thích hợp
  • Mức tin cậy bảo mật truy cập mã (CAS) (chỉ trong .NET Framework)
  • Vượt qua SecureStringkhi bạn có thể thu thập một cách an toàn thông qua tổ hợp phím người dùng.

Số lượng mã để viết để minh họa tất cả những điều này vượt quá mức cần có trong câu trả lời của StackOverflow, IMHO.

Cách tiếp cận kết hợp và dễ dàng hơn

Thay vì tự viết tất cả những điều này, hãy xem xét sử dụng thư viện SimpleImpersonation của tôi , kết hợp việc mạo danh và truy cập người dùng vào một API duy nhất. Nó hoạt động tốt trong cả cơ sở mã hiện đại và cũ hơn, với cùng một API đơn giản:

var credentials = new UserCredentials(domain, username, password);
Impersonation.RunAsUser(credentials, logonType, () =>
{
    // do whatever you want as this user.
}); 

hoặc là

var credentials = new UserCredentials(domain, username, password);
var result = Impersonation.RunAsUser(credentials, logonType, () =>
{
    // do whatever you want as this user.
    return something;
});

Lưu ý rằng nó rất giống với WindowsIdentity.RunImpersonatedAPI, nhưng không yêu cầu bạn biết bất cứ điều gì về việc xử lý mã thông báo.

Đây là API kể từ phiên bản 3.0.0. Xem readme dự án để biết thêm chi tiết. Cũng lưu ý rằng phiên bản trước của thư viện đã sử dụng API có IDisposablemẫu, tương tự như WindowsIdentity.Impersonate. Phiên bản mới hơn an toàn hơn nhiều, và cả hai vẫn được sử dụng nội bộ.


14
Điều này rất giống với mã có sẵn tại msdn.microsoft.com/en-us/l Library / Wiêu nhưng thật tuyệt vời khi thấy tất cả được liệt kê ở đây. Đơn giản và dễ dàng để kết hợp vào giải pháp của tôi. Cảm ơn rất nhiều vì đã làm tất cả các công việc khó khăn!
McArthey

1
Cảm ơn đã đăng bài này. Tuy nhiên, trong câu lệnh sử dụng, tôi đã thử dòng mã System.Security.Principal.WindowsIdentity.GetCản () này.
Chris

3
@Chris - Bạn sẽ cần sử dụng một trong các loại đăng nhập khác. Loại 9 chỉ cung cấp mạo danh trên các thông tin mạng bên ngoài. Tôi đã thử nghiệm loại 2, 3 & 8 từ ứng dụng WinForms và họ đã cập nhật chính xác hiệu trưởng hiện tại. Người ta sẽ giả sử loại 4 và 5 cũng làm, cho các ứng dụng dịch vụ hoặc hàng loạt. Xem liên kết tôi tham khảo trong bài.
Matt Johnson-Pint


4
@Sophit - Mã nguồn tham chiếu ở đây cho thấy rõ ràng Undođược gọi trong quá trình xử lý.
Matt Johnson-Pint

20

Đây có lẽ là những gì bạn muốn:

using System.Security.Principal;
using(WindowsIdentity.GetCurrent().Impersonate())
{
     //your code goes here
}

Nhưng tôi thực sự cần thêm chi tiết để giúp bạn. Bạn có thể thực hiện mạo danh với tệp cấu hình (nếu bạn đang cố gắng thực hiện việc này trên trang web) hoặc thông qua các trình trang trí phương thức (thuộc tính) nếu đó là dịch vụ WCF hoặc thông qua ... bạn hiểu ý tưởng.

Ngoài ra, nếu chúng ta đang nói về việc mạo danh một khách hàng được gọi là một dịch vụ cụ thể (hoặc ứng dụng web), bạn cần định cấu hình chính xác ứng dụng khách để nó chuyển các mã thông báo phù hợp.

Cuối cùng, nếu điều bạn thực sự muốn làm là Đoàn, bạn cũng cần thiết lập AD chính xác để người dùng và máy được tin cậy để ủy quyền.

Chỉnh sửa:
Hãy xem ở đây để xem cách mạo danh người dùng khác và để biết thêm tài liệu.


2
Mã này có vẻ như chỉ có thể mạo danh Danh tính cửa sổ hiện tại. Có cách nào để lấy đối tượng WindowsIdentity của người dùng khác không?
ashwnacharya

Liên kết trong bản chỉnh sửa của bạn ( msdn.microsoft.com/en-us/l Library / chf6fbt4.aspx - đi đến Ví dụ ở đó) thực sự là những gì tôi đang tìm kiếm!
Matt

Ồ bạn đã hướng dẫn tôi đi đúng hướng, chỉ cần một vài từ để thực hiện hành vi mạo danh bằng một tập tin cấu hình Cảm ơn bạn Esteban, saludos desde Peru
AjFmO

6

Đây là cổng vb.net của tôi về câu trả lời của Matt Johnson. Tôi đã thêm một enum cho các loại đăng nhập. LOGON32_LOGON_INTERACTIVElà giá trị enum đầu tiên hoạt động cho máy chủ sql. Chuỗi kết nối của tôi chỉ đáng tin cậy. Không có tên người dùng / mật khẩu trong chuỗi kết nối.

  <PermissionSet(SecurityAction.Demand, Name:="FullTrust")> _
  Public Class Impersonation
    Implements IDisposable

    Public Enum LogonTypes
      ''' <summary>
      ''' This logon type is intended for users who will be interactively using the computer, such as a user being logged on  
      ''' by a terminal server, remote shell, or similar process.
      ''' This logon type has the additional expense of caching logon information for disconnected operations; 
      ''' therefore, it is inappropriate for some client/server applications,
      ''' such as a mail server.
      ''' </summary>
      LOGON32_LOGON_INTERACTIVE = 2

      ''' <summary>
      ''' This logon type is intended for high performance servers to authenticate plaintext passwords.
      ''' The LogonUser function does not cache credentials for this logon type.
      ''' </summary>
      LOGON32_LOGON_NETWORK = 3

      ''' <summary>
      ''' This logon type is intended for batch servers, where processes may be executing on behalf of a user without 
      ''' their direct intervention. This type is also for higher performance servers that process many plaintext
      ''' authentication attempts at a time, such as mail or Web servers. 
      ''' The LogonUser function does not cache credentials for this logon type.
      ''' </summary>
      LOGON32_LOGON_BATCH = 4

      ''' <summary>
      ''' Indicates a service-type logon. The account provided must have the service privilege enabled. 
      ''' </summary>
      LOGON32_LOGON_SERVICE = 5

      ''' <summary>
      ''' This logon type is for GINA DLLs that log on users who will be interactively using the computer. 
      ''' This logon type can generate a unique audit record that shows when the workstation was unlocked. 
      ''' </summary>
      LOGON32_LOGON_UNLOCK = 7

      ''' <summary>
      ''' This logon type preserves the name and password in the authentication package, which allows the server to make 
      ''' connections to other network servers while impersonating the client. A server can accept plaintext credentials 
      ''' from a client, call LogonUser, verify that the user can access the system across the network, and still 
      ''' communicate with other servers.
      ''' NOTE: Windows NT:  This value is not supported. 
      ''' </summary>
      LOGON32_LOGON_NETWORK_CLEARTEXT = 8

      ''' <summary>
      ''' This logon type allows the caller to clone its current token and specify new credentials for outbound connections.
      ''' The new logon session has the same local identifier but uses different credentials for other network connections. 
      ''' NOTE: This logon type is supported only by the LOGON32_PROVIDER_WINNT50 logon provider.
      ''' NOTE: Windows NT:  This value is not supported. 
      ''' </summary>
      LOGON32_LOGON_NEW_CREDENTIALS = 9
    End Enum

    <DllImport("advapi32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)> _
    Private Shared Function LogonUser(lpszUsername As [String], lpszDomain As [String], lpszPassword As [String], dwLogonType As Integer, dwLogonProvider As Integer, ByRef phToken As SafeTokenHandle) As Boolean
    End Function

    Public Sub New(Domain As String, UserName As String, Password As String, Optional LogonType As LogonTypes = LogonTypes.LOGON32_LOGON_INTERACTIVE)
      Dim ok = LogonUser(UserName, Domain, Password, LogonType, 0, _SafeTokenHandle)
      If Not ok Then
        Dim errorCode = Marshal.GetLastWin32Error()
        Throw New ApplicationException(String.Format("Could not impersonate the elevated user.  LogonUser returned error code {0}.", errorCode))
      End If

      WindowsImpersonationContext = WindowsIdentity.Impersonate(_SafeTokenHandle.DangerousGetHandle())
    End Sub

    Private ReadOnly _SafeTokenHandle As New SafeTokenHandle
    Private ReadOnly WindowsImpersonationContext As WindowsImpersonationContext

    Public Sub Dispose() Implements System.IDisposable.Dispose
      Me.WindowsImpersonationContext.Dispose()
      Me._SafeTokenHandle.Dispose()
    End Sub

    Public NotInheritable Class SafeTokenHandle
      Inherits SafeHandleZeroOrMinusOneIsInvalid

      <DllImport("kernel32.dll")> _
      <ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)> _
      <SuppressUnmanagedCodeSecurity()> _
      Private Shared Function CloseHandle(handle As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
      End Function

      Public Sub New()
        MyBase.New(True)
      End Sub

      Protected Overrides Function ReleaseHandle() As Boolean
        Return CloseHandle(handle)
      End Function
    End Class

  End Class

Bạn cần sử dụng với một Usingcâu lệnh để chứa một số mã để chạy mạo danh.


3

Xem thêm chi tiết từ câu trả lời trước của tôi Tôi đã tạo một gói nuget Nuget

Mã trên Github

mẫu: bạn có thể sử dụng:

           string login = "";
           string domain = "";
           string password = "";

           using (UserImpersonation user = new UserImpersonation(login, domain, password))
           {
               if (user.ImpersonateValidUser())
               {
                   File.WriteAllText("test.txt", "your text");
                   Console.WriteLine("File writed");
               }
               else
               {
                   Console.WriteLine("User not connected");
               }
           }

Vieuw mã đầy đủ:

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;


/// <summary>
/// Object to change the user authticated
/// </summary>
public class UserImpersonation : IDisposable
{
    /// <summary>
    /// Logon method (check athetification) from advapi32.dll
    /// </summary>
    /// <param name="lpszUserName"></param>
    /// <param name="lpszDomain"></param>
    /// <param name="lpszPassword"></param>
    /// <param name="dwLogonType"></param>
    /// <param name="dwLogonProvider"></param>
    /// <param name="phToken"></param>
    /// <returns></returns>
    [DllImport("advapi32.dll")]
    private static extern bool LogonUser(String lpszUserName,
        String lpszDomain,
        String lpszPassword,
        int dwLogonType,
        int dwLogonProvider,
        ref IntPtr phToken);

    /// <summary>
    /// Close
    /// </summary>
    /// <param name="handle"></param>
    /// <returns></returns>
    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public static extern bool CloseHandle(IntPtr handle);

    private WindowsImpersonationContext _windowsImpersonationContext;
    private IntPtr _tokenHandle;
    private string _userName;
    private string _domain;
    private string _passWord;

    const int LOGON32_PROVIDER_DEFAULT = 0;
    const int LOGON32_LOGON_INTERACTIVE = 2;

    /// <summary>
    /// Initialize a UserImpersonation
    /// </summary>
    /// <param name="userName"></param>
    /// <param name="domain"></param>
    /// <param name="passWord"></param>
    public UserImpersonation(string userName, string domain, string passWord)
    {
        _userName = userName;
        _domain = domain;
        _passWord = passWord;
    }

    /// <summary>
    /// Valiate the user inforamtion
    /// </summary>
    /// <returns></returns>
    public bool ImpersonateValidUser()
    {
        bool returnValue = LogonUser(_userName, _domain, _passWord,
                LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
                ref _tokenHandle);

        if (false == returnValue)
        {
            return false;
        }

        WindowsIdentity newId = new WindowsIdentity(_tokenHandle);
        _windowsImpersonationContext = newId.Impersonate();
        return true;
    }

    #region IDisposable Members

    /// <summary>
    /// Dispose the UserImpersonation connection
    /// </summary>
    public void Dispose()
    {
        if (_windowsImpersonationContext != null)
            _windowsImpersonationContext.Undo();
        if (_tokenHandle != IntPtr.Zero)
            CloseHandle(_tokenHandle);
    }

    #endregion
}

2

Tôi biết rằng tôi đến bữa tiệc khá muộn, nhưng tôi cho rằng thư viện từ Phillip Allan-Harding , đây là nơi tốt nhất cho trường hợp này và những thứ tương tự.

Bạn chỉ cần một đoạn mã nhỏ như thế này:

private const string LOGIN = "mamy";
private const string DOMAIN = "mongo";
private const string PASSWORD = "HelloMongo2017";

private void DBConnection()
{
    using (Impersonator user = new Impersonator(LOGIN, DOMAIN, PASSWORD, LogonType.LOGON32_LOGON_NEW_CREDENTIALS, LogonProvider.LOGON32_PROVIDER_WINNT50))
    {
    }
}

Và thêm lớp của mình:

.NET (C #) Mạo danh với thông tin mạng

Ví dụ của tôi có thể được sử dụng nếu bạn yêu cầu đăng nhập mạo danh để có thông tin mạng, nhưng nó có nhiều tùy chọn hơn.


1
Cách tiếp cận của bạn có vẻ chung chung hơn trong khi cụ thể hơn về các tham số +1
Kerry Perret

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.