在异步调用上设置安全性

异步调用存在严重的安全风险,因为对 接收器 的回调可能不是原始应用程序或脚本的异步调用的结果。 远程连接的安全性基于客户端与远程计算机上的提供程序之间的通信加密。 在 C++ 中,可以通过调用 CoInitializeSecurity 中的身份验证级别参数设置加密。 在脚本中,在名字对象连接或 SWbemSecurity 对象上设置 AuthenticationLevel。 有关详细信息,请参阅 使用 VBScript 设置默认进程安全级别

异步调用的安全风险存在,因为 WMI 会降低回调上的身份验证级别,直到回调成功。 在传出异步调用中,客户端可以在与 WMI 的连接上设置身份验证级别。 WMI 检索客户端调用上的安全设置,并尝试使用相同的身份验证级别进行回叫。 回调始终在 RPC_C_AUTHN_LEVEL_PKT_PRIVACY 级别启动。 如果回调失败,WMI 会将身份验证级别降低到回调可以成功(如有必要)的级别 ,以RPC_C_AUTHN_LEVEL_NONE。 在身份验证服务不是 Kerberos 的本地系统中的调用上下文中,回调始终在 RPC_C_AUTHN_LEVEL_NONE返回。

最低身份验证级别 RPC_C_AUTHN_LEVEL_PKT (wbemAuthenticationLevelPktfor 脚本) 。 但是,可以指定更高的级别,例如 RPC_C_AUTHN_LEVEL_PKT_PRIVACY (wbemAuthenticationLevelPktPrivacy) 。 建议客户端应用程序或脚本将身份验证级别设置为 RPC_C_AUTHN_LEVEL_DEFAULT (wbemAuthenticationLevelDefault) ,以便将身份验证级别协商到服务器指定的级别。

HKEY_LOCAL_MACHINE\ SoftwareMicrosoftWBEMCIMOMUnsecAppAccessControlDefault\\\\ 注册表值控制 WMI 是否在回调中检查可接受的身份验证级别。 这是为脚本或Visual Basic中进行的异步调用保护接收器安全性的唯一机制。 默认情况下,此注册表项设置为零。 如果注册表项为零,则 WMI 不会验证身份验证级别。 若要保护脚本中的异步调用,请将注册表项设置为 1。 C++ 客户端可以调用 IWbemUnsecuredApartment::CreateSinkStub 来控制对接收器的访问。 默认情况下,该值在任意位置创建。

以下主题提供了设置异步调用安全性的示例:

在 C++ 中设置异步调用安全性

IWbemUnsecuredApartment::CreateSinkStub 方法类似于 IUnsecuredApartment::CreateObjectStub 方法,并在单独的进程中创建接收器,Unsecapp.exe接收回调。 但是, CreateSinkStub 方法具有 dwFlagparameter,用于指定单独的进程如何处理访问控制。

dwFlag 参数指定以下Unsecapp.exe操作之一:

  • 使用注册表项设置来确定是否检查访问权限。
  • 忽略注册表项,并始终检查访问权限。
  • 忽略注册表项,从不检查访问权限。

本主题中的代码示例需要以下#include语句才能正确编译。

#include <wbemidl.h>

以下过程介绍如何使用 IWbemUnsecuredApartment 执行异步调用。

使用 IWbemUnsecuredApartment 执行异步调用

  1. 使用调用 CoCreateInstance 创建专用进程。

    下面的代码示例调用 CoCreateInstance 来创建专用进程。

    CLSID                    CLSID_WbemUnsecuredApartment;
    IWbemUnsecuredApartment* pUnsecApp = NULL;
    
    CoCreateInstance(CLSID_WbemUnsecuredApartment, 
                     NULL, 
                     CLSCTX_LOCAL_SERVER, 
                     IID_IWbemUnsecuredApartment, 
                     (void**)&pUnsecApp);
    
  2. 实例化接收器对象。

    下面的代码示例创建新的接收器对象。

    CMySink* pSink = new CMySink;
    pSink->AddRef();
    
  3. 为接收器创建存根。

    存根是从接收器生成的包装函数。

    下面的代码示例为接收器创建存根。

    LPCWSTR          wszReserved = NULL;           
    IWbemObjectSink* pStubSink   = NULL;
    IUnknown*        pStubUnk    = NULL; 
    
    pUnsecApp->CreateSinkStub(pSink,
                              WBEM_FLAG_UNSECAPP_CHECK_ACCESS,  //Authenticate callbacks regardless of registry key
                              wszReserved,
                              &pStubSink);
    
  4. 释放接收器对象指针。

    你可以释放对象指针,因为存根现在拥有该指针。

    下面的代码示例释放对象指针。

    pSink->Release();
    
  5. 在任何异步调用中使用存根。

    调用完成后,释放本地引用计数。

    下面的代码示例在异步调用中使用存根。

    // pServices is an IWbemServices* object
    pServices->CreateInstanceEnumAsync(strClassName, 0, NULL, pStubSink);
    

    有时,在进行调用后,可能需要取消异步调用。 如果需要取消呼叫,请使用最初调用的同一指针取消调用。

    下面的代码示例介绍如何取消异步调用。

    pServices->CancelAsyncCall(pStubSink);
    
  6. 使用异步调用完成后释放本地引用计数。

    请确保仅在确认异步调用不能取消之后才释放 pStubSink 指针。 此外,不要在 WMI 释放 pSink 接收器指针后释放 pStubSink pSink 后释放 pStubSink 会创建一个循环引用计数,在该计数中,接收器和存根始终保留在内存中。 相反,释放指针的可能位置位于 IWbemObjectSink::SetStatus 调用中,WMI 发出以报告原始异步调用已完成。

  7. 完成后,使用对 Release () 的调用取消初始化 COM。

    下面的代码示例演示如何在 pUnsecApp 指针上调用 Release ()

    pUnsecApp->Release();
    

有关 CoInitializeSecurity 函数和参数的详细信息,请参阅 COM 文档。