클라이언트 사칭
사용자 애플리케이션이 WMI 공급자를 통해 시스템의 개체에서 데이터를 요청할 때, 대리 실행은 공급자가 클라이언트의 보안 수준을 나타내는 자격 증명을 제공한다는 것을 의미합니다. 사칭은 클라이언트가 시스템의 정보에 무단으로 액세스하지 못하도록 합니다.
이 항목에서는 다음 섹션에 대해 설명합니다.
- 대리 수행을 위한 공급자 등록
- 공급자 내에서 가장 수준 설정
- 공급자 보안 수준 유지 관리
- 공급자에서 액세스 거부 메시지 처리
- 부분 인스턴스 보고하기
- 부분 열거형 보고서
- 액세스 거부 코드 디버깅
- 관련 항목
WMI는 일반적으로 LocalServer 보안 컨텍스트를 사용하여 높은 보안 수준에서 관리 서비스로 실행됩니다. 관리 서비스를 사용하면 WMI가 권한 있는 정보에 액세스할 수 있는 수단을 제공합니다. 정보를 공급자를 호출할 때 WMI는 해당 SID(보안 식별자)를 공급자에게 전달하여 공급자가 동일한 높은 보안 수준에서 정보에 액세스할 수 있도록 합니다.
WMI 애플리케이션 시작 프로세스 중에 Windows 운영 체제는 WMI 애플리케이션에 프로세스를 시작한 사용자의 보안 컨텍스트를 제공합니다. 사용자의 보안 컨텍스트는 일반적으로 LocalServer보다 낮은 보안 수준이므로 사용자에게 WMI에서 사용할 수 있는 모든 정보에 액세스할 수 있는 권한이 없을 수 있습니다. 사용자 애플리케이션에서 동적 정보를 요청하면 WMI는 사용자의 SID를 해당 공급자에게 전달합니다. 적절하게 작성된 경우 공급자는 공급자 SID가 아닌 사용자 SID를 사용하여 정보에 액세스하려고 시도합니다.
공급자가 클라이언트 애플리케이션을 성공적으로 가장하려면 클라이언트 애플리케이션 및 공급자가 다음 조건을 충족해야 합니다.
- 클라이언트 애플리케이션은 COM 연결 보안 수준을 RPC_C_IMP_LEVEL_IMPERSONATE 또는 RPC_C_IMP_LEVEL_DELEGATE로 설정하여 WMI를 호출해야 합니다. 자세한 내용은 WMI 보안유지를 참조하세요.
- 공급자는 WMI에 대한 대리 공급자로 등록해야 합니다. 자세한 내용은 대리 실행을 위한 공급자 등록 및을 참조하세요.
- 공급자는 권한 있는 정보에 액세스하기 전에 클라이언트 애플리케이션의 보안 수준으로 전환해야 합니다. 자세한 내용은 공급자 내에서 가장 수준 설정 참조하세요.
- 이 정보에 대한 액세스가 거부된 경우 공급자는 오류 조건을 올바르게 처리해야 합니다. 자세한 내용은 공급자 액세스 거부 메시지 처리참조하세요.
사칭을 위한 공급자 등록
WMI는 클라이언트 애플리케이션의 SID를 대리 제공자로 등록된 제공자에게만 전달합니다. 공급자가 대리 실행을 수행하도록 설정하려면 공급자 등록 프로세스를 수정해야 합니다.
다음 절차에서는 가장을 위해 공급자를 등록하는 방법을 설명합니다. 이 절차에서는 등록 프로세스를 이미 이해하고 있다고 가정합니다. 등록 프로세스에 대한 자세한 내용은 공급자등록을 참조하세요.
대리를 위한 공급자를 등록하려면
공급자를 나타내는 __Win32Provider 클래스의 ImpersonationLevel 속성을 1로 설정합니다.
ImpersonationLevel 속성은 공급자가 인퍼소네이션을 지원하는지 여부를 문서화합니다. ImpersonationLevel 0으로 설정하면 공급자가 클라이언트를 가장하지 않고 WMI와 동일한 사용자 컨텍스트에서 요청된 모든 작업을 수행합니다. ImpersonationLevel을 1로 설정하면 공급자가 클라이언트를 대신하여 수행된 작업을 확인하기 위해 대리 호출을 사용하게 됩니다.
동일한 __Win32Provider 클래스의 PerUserInitialization 속성을 TRUE로 설정합니다.
메모
__Win32Provider 속성 InitializeAsAdminFirst 값을 TRUE로 설정하여 공급자를 등록하는 경우, 공급자는 초기화 단계에서만 관리 수준 스레드 보안 토큰을 사용합니다. CoImpersonateClient 호출은 실패하지 않지만 공급자는 클라이언트가 아닌 WMI의 보안 컨텍스트를 사용합니다.
다음 코드 예제에서는 대리 실행을 위해 공급자를 등록하는 방법을 보여줍니다.
instance of __Win32Provider
{
CLSID = "{FD4F53E0-65DC-11d1-AB64-00C04FD9159E}";
ImpersonationLevel = 1;
Name = "MS_NT_EVENTLOG_PROVIDER";
PerUserInitialization = TRUE;
};
공급자에서 대리 수준 설정
__Win32Provider 클래스 속성 ImpersonationLevel 1로 설정된 공급자를 등록하는 경우 WMI는 공급자를 호출하여 다양한 클라이언트를 가장합니다. 이러한 호출을 처리하려면 IWbemServices 인터페이스 구현에서 CoImpersonateClient 및 CoRevertToSelf COM 함수를 사용합니다.
CoImpersonateClient 함수를 사용하면 서버가 호출한 클라이언트를 가장할 수 있습니다. IWbemServices구현에 CoImpersonateClient 호출하면 공급자가 클라이언트의 스레드 토큰과 일치하도록 공급자의 스레드 토큰을 설정하고 클라이언트를 가장할 수 있습니다. coImpersonateClient 를 호출하지 않으면, 공급자가 관리자 권한 수준으로 코드를 실행함으로써 잠재적인 보안 문제의 위험성을 초래할 수 있습니다. 공급자가 일시적으로 관리자 역할을 하거나 액세스 검사를 수동으로 수행해야 하는 경우 CoRevertToSelf호출합니다.
다른 CoImpersonateClient와는 달리, CoRevertToSelf는 스레드 가장 수준을 다루는 COM 함수입니다. 이 경우 CoRevertToSelf 권한 수준을 원래의 권한 설정으로 다시 변경합니다. 일반적으로 제공자는 최초에 관리자로서, 호출자가 나타내는 호출인지 또는 자신의 호출인지에 따라 CoImpersonateClient와 CoRevertToSelf 사이를 번갈아 전환합니다. 최종 사용자에게 보안 허점을 노출하지 않도록 이러한 호출을 올바르게 배치하는 것은 공급자의 책임입니다. 예를 들어 공급자는 가장된 코드 시퀀스 내에서만 네이티브 Windows 함수를 호출해야 합니다.
메모
coImpersonateClient및 CoRevertToSelf목적은 공급자에 대한 보안을 설정하는 것입니다. 가장 시도가 실패한 것으로 확인되면, IWbemObjectSink::SetStatus를 통해 적절한 완료 코드를 WMI에 반환해야 합니다. 자세한 내용은 공급자 의액세스 거부 메시지 처리에 관해 참조하세요.
공급자에서 보안 수준 유지 관리
공급자는 IWbemServices 구현에서 CoImpersonateClient 한 번 호출할 수 없으며 공급자 기간 동안 가장 자격 증명이 그대로 유지된다고 가정합니다. 대신 구현 과정에서 CoImpersonateClient 여러 번 호출하여 WMI가 자격 증명을 변경하지 못하도록 합니다.
공급자에 대한 대역 설정의 주요 우려 사항은 재진입입니다. 이 컨텍스트에서 재진입은 서비스 제공자가 정보를 얻기 위해 WMI에 요청을 하고, WMI가 다시 서비스 제공자와의 연결을 통해 응답할 때까지 기다리는 경우를 의미합니다. 기본적으로 실행 스레드는 나중에 코드를 다시 입력하기 위해 공급자 코드를 남깁니다. 재진입은 COM 디자인의 일부이며 일반적으로 문제가 되지 않습니다. 그러나 실행 스레드가 WMI에 들어가면 스레드는 WMI의 권한 수준을 사용합니다. 스레드가 공급자로 돌아오면, CoImpersonateClient에 대한 다른 호출로 가장 수준을 다시 설정해야 합니다.
공급자의 보안 허점으로부터 자신을 보호하려면 클라이언트를 가장하는 동안에만 WMI에 대한 재진입 호출을 수행해야 합니다. 즉, CoImpersonateClient 호출하고 CoRevertToSelf호출하기 전에 WMI를 호출해야 합니다. CoRevertToSelf 때문에 권한 가장이 WMI가 실행되는 사용자 수준(일반적으로 LocalSystem)으로 설정되므로 CoRevertToSelf 호출 후 WMI에 대한 재진입 요청은 사용자와 호출된 공급자에게 있어야 하는 것보다 훨씬 더 많은 기능을 제공할 수 있습니다.
메모
시스템 함수 또는 다른 인터페이스 메서드를 호출하는 경우 호출 컨텍스트가 유지 관리되지 않습니다.
공급자에서 액세스 거부 메시지 처리
대부분의 액세스 거부 오류 메시지는 클라이언트가 액세스 권한이 없는 클래스 또는 정보를 요청할 때 표시됩니다. 공급자가 액세스 거부 오류 메시지를 WMI에 반환하고 WMI가 이를 클라이언트에 전달하는 경우 클라이언트는 정보가 있음을 유추할 수 있습니다. 경우에 따라 보안 위반이 될 수 있습니다. 따라서 공급자가 메시지를 클라이언트에 전파해서는 안 됩니다. 대신 공급자가 제공한 클래스 집합은 노출되지 않아야 합니다. 마찬가지로 동적 인스턴스 공급자는 기본 데이터 원본을 호출하여 액세스 거부 메시지를 처리하는 방법을 결정해야 합니다. 이 철학을 WMI 환경에 복제하는 것은 공급자의 책임입니다. 자세한 내용은 부분 인스턴스 보고 과 부분 열거형 보고 을 참조하세요.
공급자가 액세스 거부 메시지를 처리하는 방법을 결정할 때 코드를 작성하고 디버그해야 합니다. 디버깅하는 동안 낮은 권한 위임으로 인한 거부와 코드의 오류로 인한 거부를 구분하는 것이 편리합니다. 코드에서 간단한 테스트를 사용하여 차이를 확인할 수 있습니다. 자세한 내용은 액세스 거부 코드 디버깅을 참조하세요.
부분 인스턴스 보고
액세스 거부 메시지의 한 가지 일반적인 발생은 WMI가 인스턴스를 채우기 위해 모든 정보를 제공할 수 없는 경우입니다. 예를 들어 클라이언트는 하드 디스크 드라이브 개체를 볼 권한이 있지만 하드 디스크 드라이브 자체에서 사용할 수 있는 공간을 확인할 권한이 없을 수 있습니다. 공급자가 액세스 위반으로 인해 인스턴스를 속성으로 완전히 채울 수 없는 경우 공급자가 상황을 처리하는 방법을 결정해야 합니다.
WMI는 인스턴스에 부분적으로 액세스할 수 있는 클라이언트에 대한 단일 응답이 필요하지 않습니다. 대신 WMI 버전 1.x에서는 공급자가 다음 옵션 중 하나를 허용합니다.
WBEM_E_ACCESS_DENIED로 인해 전체 작업이 실패하고 인스턴스를 반환하지 않습니다.
WBEM_E_ACCESS_DENIED함께 오류 개체를 반환하여 거부 이유를 설명합니다.
사용 가능한 모든 속성을 반환하고 사용할 수 없는 속성을 NULL채웁니다.
메모
WBEM_E_ACCESS_DENIED를 반환해야 할 때, 기업에 보안 허점이 생기지 않도록 반드시 확인하십시오.
부분 열거형 보고
액세스 위반 오류가 일반적으로 발생하는 또 다른 경우는 WMI가 모든 열거된 객체를 반환할 수 없을 때입니다. 예를 들어 클라이언트는 모든 로컬 네트워크 컴퓨터 개체를 볼 수 있는 액세스 권한이 있지만 도메인 외부에서 컴퓨터 개체를 볼 수 있는 액세스 권한이 없을 수 있습니다. 공급자는 액세스 위반으로 인해 열거를 완료할 수 없는 상황을 처리하는 방법을 결정해야 합니다.
인스턴스 공급자와 마찬가지로 WMI는 부분 열거형에 대한 단일 응답이 필요하지 않습니다. 대신 WMI 버전 1.x에서는 공급자가 다음 옵션 중 하나를 허용합니다.
공급자가 액세스할 수 있는 모든 인스턴스에 대해 WBEM_S_NO_ERROR 값을 반환하십시오.
이 옵션을 사용하는 경우 사용자는 일부 인스턴스를 사용할 수 없다는 사실을 인식하지 않습니다. 행 수준 보안이 있는 SQL(구조적 쿼리 언어)을 사용하는 공급자와 같은 여러 공급자는 호출자의 보안 수준을 사용하여 결과 집합을 정의하여 성공적인 부분 결과를 반환합니다.
WBEM_E_ACCESS_DENIED를 사용하여 작업 전체를 실패로 처리하고 인스턴스를 반환하지 않습니다.
공급자는 필요에 따라 클라이언트에 상황을 설명하는 오류 개체를 포함할 수 있습니다. 일부 공급자는 데이터 원본에 순차적으로 접근하며, 열거 도중 중간에서야 접근 거부를 경험할 수 있습니다.
액세스할 수 있는 모든 인스턴스를 반환하지만 WBEM_S_ACCESS_DENIED없음 상태 코드도 반환합니다.
공급자는 열거 중에 거부에 유의해야 하며 인스턴스를 계속 제공하여 nonerror 상태 코드로 마무리할 수 있습니다. 공급자는 첫 번째 거부 시 열거형을 종료하도록 선택할 수도 있습니다. 이 옵션의 근거는 공급자별로 검색 패러다임이 다르다는 것입니다. 액세스 위반을 검색하기 전에 공급자가 이미 인스턴스를 배달했을 수 있습니다. 일부 공급자는 다른 인스턴스를 계속 제공하도록 선택할 수 있으며 다른 공급자는 종료하려고 할 수 있습니다.
COM의 구조로 인해 오류 개체를 제외하고 오류 중에는 정보를 다시 마샬링할 수 없습니다. 따라서 정보와 오류 코드를 모두 반환할 수 없습니다. 정보를 반환하도록 선택하는 경우 nonerror 상태 코드를 대신 사용해야 합니다.
액세스 거부 코드 디버깅
일부 애플리케이션은 RPC_C_IMP_LEVEL_IMPERSONATE미만의 가장 권한 수준을 사용할 수 있습니다. 이 경우 클라이언트 애플리케이션을 위한 공급자가 수행한 대부분의 대리 호출이 실패합니다. 공급자를 성공적으로 디자인하고 구현하려면 이 아이디어를 염두에 두어야 합니다.
기본적으로 공급자에 액세스할 수 있는 또 다른 유일한 가장 수준은 RPC_C_IMP_LEVEL_IDENTIFY입니다. 클라이언트 애플리케이션이 RPC_C_IMP_LEVEL_IDENTIFY사용하는 경우 CoImpersonateClient 오류 코드를 반환하지 않습니다. 대신 공급자는 식별 목적으로만 클라이언트를 가장합니다. 따라서 공급자가 호출하는 대부분의 Windows 메서드는 액세스 거부 메시지를 반환합니다. 사용자가 부적절한 작업을 수행할 수 없으므로 실제로는 무해합니다. 그러나 공급자를 개발하는 동안 클라이언트가 실제로 가장되었는지 여부를 파악하는 것이 유용할 수 있습니다.
코드를 제대로 컴파일하려면 다음 참조 및 #include 문이 필요합니다.
#define _WIN32_DCOM
#include <iostream>
using namespace std;
#include <wbemidl.h>
다음 코드 예제에서는 공급자가 클라이언트 애플리케이션을 성공적으로 가장했는지 여부를 확인하는 방법을 보여줍니다.
DWORD dwImp = 0;
HANDLE hThreadTok;
DWORD dwBytesReturned;
BOOL bRes;
// You must call this before trying to open a thread token!
CoImpersonateClient();
bRes = OpenThreadToken(
GetCurrentThread(),
TOKEN_QUERY,
TRUE,
&hThreadTok
);
if (bRes == FALSE)
{
printf("Unable to read thread token (%d)\n", GetLastError());
return 0;
}
bRes = GetTokenInformation(
hThreadTok,
TokenImpersonationLevel,
&dwImp,
sizeof(DWORD),
&dwBytesReturned
);
if (!bRes)
{
printf("Unable to read impersonation level\n");
CloseHandle(hThreadTok);
return 0;
}
switch (dwImp)
{
case SecurityAnonymous:
printf("SecurityAnonymous\n");
break;
case SecurityIdentification:
printf("SecurityIdentification\n");
break;
case SecurityImpersonation:
printf("SecurityImpersonation\n");
break;
case SecurityDelegation:
printf("SecurityDelegation\n");
break;
default:
printf("Error. Unable to determine impersonation level\n");
break;
}
CloseHandle(hThreadTok);
관련 항목