알아두면 좋은팁 그 두번째 Impersonate 입니다.
발생시나리오
- NAS 장비에 이미지를 공유를 통해 올리고, 별도의 도메인으로 서비스 되도록 한다.
- 웹 사이트는 네트워크 서비스 계정으로 돌아가며, NAS 장비 공유의 계정은 별도로 받았다.
1차 꼼수처리
- 웹 사이트 구성할때 네트워크 서비스 계정을 사용하지 않고, 별도로 받은 NAS 장비 계정으로 사이트를 만든다.
- ;;;;;;;
아무래도 안되겠다.
- 뭔가 마음이 놓이질 않는다. 그럴리야 없겠지만, 보안상 문제가 생기지는 않을까? ㅡㅡ;
- 뭔가 양껏 불안하다 ;; 결국 구글링
- Impersonate 라는 것을 찾았다.
Impersonate 란
- 네트워크 서비스 계정 -> NTFS 권한을 가진 계정으로 실행할 수 있도록 Windows API 를 사용하여 변경 -> 다시 네트워크 서비스 계정
- 네트워크 서비스 계정은 사이트 일때 이고, C# 어플리케이션이나 C/S 시스템에선 다를 수 있겠죵~
- 뭐 결국은 Windows API로 실행 계정을 변경 했다가 다시 돌린다는 것입니다.
이하 아래는 제가 사용한 Impersonate 클래스 소스 원본 입니다. 파란색 글은 설명 입니다. ^^;
============================================= 아 래. =============================================
using System; using System.Security.Principal; using System.Runtime.InteropServices;
namespace Test.Impersonate // 네임스페이스를 알맞게... ^^; {
// region 구문은 API에 사용할 변수들을 선언 합니다. 뭔지는 잘 모릅니다. 여튼 없으믄 안됩니다. ;; #region LogonType, LogonProvider
/// <summary> /// LogonUser API에서 사용하는 로그온 타입 /// </summary> public enum LogonType { LOGON32_LOGON_INTERACTIVE = 2, LOGON32_LOGON_NETWORK = 3, LOGON32_LOGON_BATCH = 4, LOGON32_LOGON_SERVICE = 5, LOGON32_LOGON_UNLOCK = 7, LOGON32_LOGON_NETWORK_CLEARTEXT = 8, LOGON32_LOGON_NEW_CREDENTIALS = 9 }
/// <summary> /// LogonUser API에서 사용하는 로그온 프로바이더 /// </summary> public enum LogonProvider { LOGON32_PROVIDER_DEFAULT = 0, LOGON32_PROVIDER_WINNT35 = 1, LOGON32_PROVIDER_WINNT40 = 2, LOGON32_PROVIDER_WINNT50 = 3 }
#endregion
public class ImpersonateService {
// 사용해야하는 Windows API 의 선언 역시 내용 잘 모릅니다. ;; private WindowsImpersonationContext impersonationContext;
// Win32 Security API [DllImport("advapi32.dll", SetLastError = true)] public static extern int LogonUser(String lpszUserName, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern bool RevertToSelf();
// Win32 Kernel API [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public extern static bool CloseHandle(IntPtr handle);
// 아~ 주석처리가 정말 아름답게 되어 있습니다. 지정한 사용자 계정으로 Impersonate 를 시작한답니다.
// 도메인은 서버가 도메인 구성이 되어 있을 경우에만 적절하게 넣으면 됩니다.
// 도메인 구성이 되어 있지 않을경우 공백... 뭐 아무거나 넣어도 잘 알아서 처리하더군요 ;; 역시 MS
/// <summary> /// 지정한 사용자 계정으로 Impersonate를 시작한다. /// </summary> /// <param name="userName">계정명</param> /// <param name="domain">도메인</param> /// <param name="password">비밀번호</param> /// <returns>성공여부</returns> public bool impersonateValidUser(string userName, string domain, string password) { WindowsIdentity tempWindowsIdentity; IntPtr token = IntPtr.Zero; IntPtr tokenDuplicate = IntPtr.Zero;
if (RevertToSelf()) { if (LogonUser(userName, domain, password, (int)LogonType.LOGON32_LOGON_INTERACTIVE, (int)LogonProvider.LOGON32_PROVIDER_DEFAULT, ref token) != 0) { if (DuplicateToken(token, 2, ref tokenDuplicate) != 0) { tempWindowsIdentity = new WindowsIdentity(tokenDuplicate); impersonationContext = tempWindowsIdentity.Impersonate(); if (impersonationContext != null) { CloseHandle(token); CloseHandle(tokenDuplicate); return true; } } } }
if (token != IntPtr.Zero) CloseHandle(token); if (tokenDuplicate != IntPtr.Zero) CloseHandle(tokenDuplicate); return false; }
// 다시 원래 실행하던 계정으로 돌립니다.
// 실행 페이지 등에서 이 클래스를 using 문으로 감싸주고 언로드에 실행시키면 굿!!
// 전 그냥 public 으로 선언해 주고 명시적으로 이 함수 호출해 줬습니다.
/// <summary> /// Impersonate를 revoke하여 종료한다. /// </summary> public void undoImpersonation() { impersonationContext.Undo(); } } }
|