Kimliğe bürünme .NET'te nasıl yapıyorsunuz?


139

.NET'te bir kullanıcının kimliğine bürünmenin basit bir yolu var mı?

Şimdiye kadar kod sınıfından bu sınıfı tüm kimliğe bürünme gereksinimlerim için kullanıyorum.

.NET Framework kullanarak bunu yapmanın daha iyi bir yolu var mı?

Kimliğe bürünmek istediğim kimliği temsil eden bir kullanıcı kimlik ayarım var (kullanıcı adı, şifre, etki alanı adı).


1
Daha spesifik olabilir misiniz? Kutudan çıkma taklit etmenin tonlarca yolu var.
Esteban Araya

Yanıtlar:


60

İşte .NET kimliğe bürünme kavramlarına bazı iyi genel bakış.

Temel olarak .NET çerçevesindeki kutunun dışında olan bu sınıflardan yararlanacaksınız:

Kod genellikle uzun sürebilir ve bu yüzden süreci basitleştirmeye çalışan referans gibi birçok örnek görüyorsunuz.


4
Kimliğe bürünme işleminin gümüş madde işareti olmadığını ve bazı API'ların kimliğe bürünme ile çalışacak şekilde tasarlanmadığını unutmayın.
Lex Li

Hollandalı programcının blogundaki bağlantı mükemmeldi. Kimliğe bürünme konusunda sunulan diğer tekniklerden çok daha sezgisel bir yaklaşım.
code4life

296

.NET alanındaki "Kimliğe bürünme" genellikle kodun belirli bir kullanıcı hesabı altında çalıştırılması anlamına gelir. Bu kullanıcı hesabına bir kullanıcı adı ve parola ile erişmekten biraz ayrı bir kavramdır, ancak bu iki fikir sık ​​sık birlikte eşleşir. Her ikisini de açıklayacağım ve sonra bunları dahili olarak kullanan SimpleImpersonation kütüphanemi nasıl kullanacağımı açıklayacağım .

Impersonation

Kimliğe bürünme API'leri, System.Security.Principalad alanı üzerinden .NET'te sağlanır :

  • Genel WindowsIdentity.RunImpersonatedolarak, kullanıcı hesabının belirtecine yönelik bir tanıtıcı kabul eden ve daha sonra kodun yürütülmesi için ya da bir Actionveya kabul eden daha yeni kod (.NET 4.6+, .NET Core, vb.) Kullanılmalıdır Func<T>.

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

    veya

    var result = WindowsIdentity.RunImpersonated(tokenHandle, () =>
    {
        // do whatever you want as this user.
        return result;
    });
  • Eski kod, WindowsIdentity.Impersonatebir WindowsImpersonationContextnesneyi almak için yöntemi kullandı . Bu nesne uygulanır IDisposable, bu nedenle genellikle bir usingbloktan çağrılmalıdır .

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

    Bu API hala .NET Framework'te mevcut olsa da, genellikle bundan kaçınılmalıdır ve .NET Core veya .NET Standard'da mevcut değildir.

Kullanıcı Hesabına Erişim

Windows'ta bir kullanıcı hesabına erişmek için kullanıcı adı ve parola kullanma LogonUserAPI'sı Win32 yerel API'sıdır. Şu anda çağırmak için yerleşik bir .NET API yok, bu yüzden bir P / Invoke başvurmak gerekir.

[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);

Bu temel çağrı tanımıdır, ancak bunu aslında üretimde kullanmayı düşünecek çok daha fazla şey vardır:

  • "Güvenli" erişim desenine sahip bir tutamak elde edilmesi.
  • Yerel tutamakları uygun şekilde kapatma
  • Kod erişim güvenliği (CAS) güven düzeyleri (yalnızca .NET Framework'te)
  • SecureStringKullanıcı tuş vuruşlarıyla güvenli bir şekilde toplayabildiğiniz zaman geçme .

Tüm bunları göstermek için yazılacak kod miktarı, bir StackOverflow cevabı IMHO'da olması gerekenin ötesindedir.

Kombine ve Daha Kolay Bir Yaklaşım

Tüm bunları kendiniz yazmak yerine , kimliğe bürünme ve kullanıcı erişimini tek bir API'de birleştiren SimpleImpersonation kitaplığımı kullanmayı düşünün . Aynı basit API ile hem modern hem de eski kod tabanlarında iyi çalışır:

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

veya

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

WindowsIdentity.RunImpersonatedAPI'ya çok benzediğini , ancak jeton tanıtıcıları hakkında hiçbir şey bilmenizi gerektirmediğini unutmayın.

3.0.0 sürümündeki API budur. Daha fazla ayrıntı için proje benioku dosyasına bakın. Ayrıca, kitaplığın önceki bir sürümünün IDisposable, benzer bir API kullandığını unutmayın WindowsIdentity.Impersonate. Yeni sürüm çok daha güvenli ve her ikisi de hala dahili olarak kullanılıyor.


14
Bu, msdn.microsoft.com/en-us/library/… adresindeki koda çok benzer, ancak burada listelenenlerin hepsini görmek inanılmaz derecede güzel. Kolay ve benim çözümüme dahil etmek kolaydır. Tüm zor işleri yaptığınız için çok teşekkürler!
McArthey

1
Bunu gönderdiğiniz için teşekkürler. Ancak, kullanım deyiminde System.Security.Principal.WindowsIdentity.GetCurrent () kodun bu satırını denedim. Ad ve sonuç, Kimliğe Bürünme yapıcısına ilettiğim kullanıcıyla değil, yalnızca oturum açtığım kullanıcı adı oldu.
Chris

3
@Chris - Diğer giriş türlerinden birini kullanmanız gerekir. Tip 9 yalnızca giden ağ kimlik bilgileri üzerinde kimliğe bürünme sağlar. Bir WinForms uygulamasından 2, 3 ve 8 tiplerini test ettim ve mevcut anaparayı düzgün bir şekilde güncellediler. Birisi, servis veya parti uygulamaları için tip 4 ve 5'in de var olduğunu varsayar. Mesajda referans verdiğim bağlantıya bakın.
Matt Johnson-Pint


4
@Sophit - Buradaki referans kaynak koduUndo bertaraf sırasında çağrıldığını açıkça göstermektedir .
Matt Johnson-Pint

20

Muhtemelen istediğiniz şey budur:

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

Ama size yardımcı olmak için daha fazla ayrıntıya ihtiyacım var. Bir yapılandırma dosyasıyla kimliğe bürünme yapabilirsiniz (bunu bir web sitesinde yapmaya çalışıyorsanız) veya bir WCF hizmeti ise yöntem dekoratörleri (öznitelikler) aracılığıyla veya ... aracılığıyla fikir edinebilirsiniz.

Ayrıca, belirli bir hizmeti (veya web uygulamasını) çağıran bir istemcinin kimliğine bürünme hakkında konuşuyorsak, istemciyi uygun belirteçleri geçecek şekilde doğru yapılandırmanız gerekir.

Son olarak, gerçekten yapmak istediğiniz şey Temsilci ise, kullanıcıların ve makinelerin temsilci seçme için güvenilir olması için AD'yi doğru şekilde ayarlamanız gerekir.

Düzenleme: Farklı bir kullanıcıyı nasıl taklit edeceğinizi görmek ve daha fazla dokümantasyon için buraya
göz atın .


2
Bu kod, yalnızca Geçerli Windows kimliğini taklit edebilir. Başka bir kullanıcının WindowsIdentity nesnesini almanın bir yolu var mı?
ashwnacharya

Düzenlemenizdeki bağlantı ( msdn.microsoft.com/en-us/library/chf6fbt4.aspx - Örneklere gidin ) gerçekten aradığım şey!
Matt

Vaov! bana doğru yönde rehberlik etti , bir yapılandırma dosyası ile sihirli kimliğe bürünme yapmak için sadece birkaç kelime gerekiyordu Teşekkür ederim Esteban, saludos desde Peru
AjFmO

6

İşte Matt Johnson'ın cevabı vb.net portum. Oturum açma türleri için bir numaralandırma ekledim. LOGON32_LOGON_INTERACTIVEsql sunucusu için çalışan ilk enum değeriydi. Bağlantı dizem sadece güvenilirdi. Bağlantı dizesinde kullanıcı adı / şifre yok.

  <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

Bir ile Kullanım gerek Usingtaklit çalıştırabilen bazı kod içeriyor açıklamada.


3

Önceki cevabımdan daha fazla ayrıntı görüntüle Nuget nuget paketi oluşturdum

Github Kodu

örnek: kullanabilirsiniz:

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

Tam kod 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

Partiye oldukça geç kaldığımı biliyorum, ama Phillip Allan-Harding'in kütüphanesinin, bu dava ve benzerleri için en iyisi olduğunu düşünüyorum.

Bunun gibi küçük bir kod parçasına ihtiyacınız var:

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

Ve sınıfını ekleyin:

Ağ Kimlik Bilgileri ile .NET (C #) Kimliğe Bürünme

Örneğim, kimliğe bürünmüş oturum açmanın ağ kimlik bilgilerine sahip olmasını istiyorsanız, ancak daha fazla seçeneğe sahipse kullanılabilir.


1
Yaklaşımınız parametrelerde daha spesifik olurken daha genel görünüyor +1
Kerry Perret

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.