.NET에서 가장을 어떻게합니까?


139

.NET에서 사용자를 가장하는 간단한 방법이 있습니까?

지금까지 나는 모든 가장 요구 사항에 대해 코드 프로젝트 에서이 클래스를 사용 했습니다 .

.NET Framework를 사용하여 더 좋은 방법이 있습니까?

가장해야 할 자격 증명을 나타내는 사용자 자격 증명 세트 (사용자 이름, 암호, 도메인 이름)가 있습니다.


1
좀 더 구체적으로 말씀해 주시겠습니까? 즉시 가장하는 방법에는 여러 가지가 있습니다.
Esteban Araya

답변:


60

다음은 .NET 가장 개념에 대한 좋은 개요입니다.

기본적으로 .NET 프레임 워크에서 즉시 사용 가능한 이러한 클래스를 활용합니다.

코드는 종종 길어질 수 있으므로 프로세스를 단순화하려고 참조하는 것과 같은 많은 예제가 표시됩니다.


4
가장은 은총 알이 아니며 일부 API는 가장을 사용하도록 설계되지 않았습니다.
Lex Li

네덜란드 프로그래머 블로그의 링크는 훌륭했습니다. 제시된 다른 기술보다 가장 가장 직관적 인 접근 방식입니다.
code4life

296

.NET 공간에서 "명의 가장"은 일반적으로 특정 사용자 계정으로 코드를 실행하는 것을 의미합니다. 이 두 아이디어는 자주 짝을 이루어도 사용자 이름과 비밀번호를 통해 해당 사용자 계정에 액세스하는 것과는 별개의 개념입니다. 둘 다 설명 하고 내부적으로 사용하는 SimpleImpersonation 라이브러리 를 사용하는 방법을 설명합니다 .

인격화

가장을위한 API는 System.Security.Principal네임 스페이스 를 통해 .NET에서 제공됩니다 .

  • 최신 코드 (.NET 4.6+, .NET Core 등)는 일반적으로을 사용해야합니다. WindowsIdentity.RunImpersonated이 경우 사용자 계정의 토큰에 대한 핸들을 허용 한 다음 Action또는 Func<T>코드가 실행될 수 있습니다.

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

    또는

    var result = WindowsIdentity.RunImpersonated(tokenHandle, () =>
    {
        // do whatever you want as this user.
        return result;
    });
  • 이전 코드는이 WindowsIdentity.Impersonate메소드를 사용하여 WindowsImpersonationContext객체 를 검색했습니다 . 이 객체는를 구현 IDisposable하므로 일반적으로 using블록 에서 호출해야합니다 .

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

    이 API는 여전히 .NET Framework에 있지만 일반적으로 사용하지 말아야하며 .NET Core 또는 .NET Standard에서는 사용할 수 없습니다.

사용자 계정에 액세스

Windows에서 사용자 계정에 액세스하기 위해 사용자 이름과 비밀번호를 사용하기위한 LogonUserAPI는 Win32 기본 API입니다. 현재이를 호출하기위한 기본 제공 .NET API가 없으므로 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);

이것이 기본 호출 정의이지만 실제로 프로덕션에서 사용하는 것을 고려해야 할 것이 더 많습니다.

  • "안전한"액세스 패턴을 가진 핸들 얻기
  • 기본 핸들을 적절하게 닫기
  • CAS (코드 액세스 보안) 트러스트 수준 (.NET Framework 만 해당)
  • SecureString사용자 키 입력을 통해 안전하게 수집 할 수있을 때 전달 합니다.

이 모든 것을 설명하기 위해 작성해야 할 코드의 양은 IMHO.

결합되고 더 쉬운 접근법

이 모든 것을 직접 작성하는 대신 가장과 사용자 액세스를 단일 API로 결합한 SimpleImpersonation 라이브러리를 사용해보십시오 . 동일한 간단한 API를 사용하여 최신 및 기존 코드 기반에서 모두 잘 작동합니다.

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

또는

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

WindowsIdentity.RunImpersonatedAPI 와 매우 유사 하지만 토큰 핸들에 대해 알 필요가 없습니다.

이것은 버전 3.0.0부터 API입니다. 자세한 내용은 프로젝트 추가 정보를 참조하십시오. 또한 이전 버전의 라이브러리는 IDisposable패턴과 유사한 API를 사용했습니다 WindowsIdentity.Impersonate. 최신 버전이 훨씬 안전하며 둘 다 여전히 내부적으로 사용됩니다.


14
이것은 msdn.microsoft.com/en-us/library/ 에있는 코드와 매우 유사 하지만 여기에 나열된 모든 코드 를 보는 것은 매우 좋습니다. 내 솔루션에 간단하고 쉽게 통합 할 수 있습니다. 모든 노력을 해주셔서 감사합니다!
McArthey 2019

1
이것을 게시 해 주셔서 감사합니다. 그러나 using 문 에서이 코드 줄 System.Security.Principal.WindowsIdentity.GetCurrent (). Name을 시도했는데 결과는 가장 생성자에 전달한 사용자 이름이 아닌 로그인 한 사용자 이름이었습니다.
Chris

3
@Chris-다른 로그인 유형 중 하나를 사용해야합니다. 유형 9는 아웃 바운드 네트워크 자격 증명에서만 가장을 제공합니다. WinForms 앱에서 유형 2, 3 및 8을 테스트했으며 현재 주체를 올바르게 업데이트합니다. 서비스 또는 배치 애플리케이션의 경우 유형 4 및 5도 가정합니다. 게시물에서 참조한 링크를 참조하십시오.
Matt Johnson-Pint

2
@Sophit- 이미하고 있습니다.
Matt Johnson-Pint

4
@Sophit-여기서 참조 소스 코드Undo폐기 중에 호출되는 것을 명확하게 보여줍니다 .
Matt Johnson-Pint

20

이것은 아마도 당신이 원하는 것입니다 :

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

하지만 도움이 되려면 더 자세한 정보가 필요합니다. 구성 파일 (웹 사이트 에서이 작업을 수행하려는 경우) 또는 WCF 서비스 인 경우 메소드 데코레이터 (속성)를 통해 가장을 수행하거나 아이디어를 얻을 수 있습니다.

또한 특정 서비스 (또는 웹앱)를 호출 한 클라이언트를 가장하는 경우 적절한 토큰을 전달하도록 클라이언트를 올바르게 구성해야합니다.

마지막으로 실제로 원하는 작업이 위임인 경우 사용자와 컴퓨터가 위임 할 수 있도록 AD를 올바르게 설정해야합니다.

편집 :
여기에 다른 사용자를 가장하는 방법을 참조 추가 문서를 위해 할 수 있습니다.


2
이 코드는 현재 Windows ID 만 가장 할 수있는 것처럼 보입니다. 다른 사용자의 WindowsIdentity 개체를 얻는 방법이 있습니까?
ashwnacharya

편집 링크 ( msdn.microsoft.com/en-us/library/chf6fbt4.aspx- 예제 로 이동 )는 실제로 내가 찾던 것입니다!
Matt

와! 당신은 옳은 방향으로 나를 안내했고 , 구성 파일로 마술 가장을 하기 위해 몇 마디 만 필요했습니다. 감사합니다 Esteban,
saludos

6

다음은 Matt Johnson의 답변에 대한 vb.net 포트입니다. 로그온 유형에 대한 열거 형을 추가했습니다. LOGON32_LOGON_INTERACTIVESQL Server에서 작동 한 첫 번째 열거 형 값이었습니다. 내 연결 문자열을 신뢰했습니다. 연결 문자열에 사용자 이름 / 비밀번호가 없습니다.

  <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

Using가장을 실행하기 위해 일부 코드를 포함 하려면 명령문 과 함께 사용해야 합니다.


3

이전 답변에서 더 자세한 내용보기 nuget 패키지를 만들었습니다.

Github의 코드

샘플 : 사용 가능 :

           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 :

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

나는 파티에 꽤 늦었다는 것을 알고 있지만 필립 앨런 하딩 (Phillip Allan-Harding) 의 도서관은 이 사건과 비슷한 도서관 에 가장 적합하다고 생각합니다.

다음과 같은 작은 코드 조각 만 있으면됩니다.

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))
    {
    }
}

그리고 그의 수업을 추가하십시오 :

네트워크 자격 증명을 사용한 .NET (C #) 가장

가장 된 로그온에 네트워크 자격 증명이 필요하지만 더 많은 옵션이있는 경우이 예를 사용할 수 있습니다.


1
매개 변수 +1에 대해보다 구체적으로 설명하면서 접근 방식이 더 일반적으로 보입니다
Kerry Perret

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.