掩蔽 (COM)
掩蔽是一种 COM 安全功能,用于确定模拟期间客户端向对服务器发送的标识。 设置掩蔽时,中间服务器会掩蔽自己的标识,并将客户端的标识呈现给代表客户端调用的服务器。 实际上,服务器看到的客户端标识是与代理关联的标识。 代理的标识由几个因素决定,其中一个因素是设置的掩蔽类型(如果有)。 Schannel 安全提供程序不支持掩蔽。
以下主题提供有关掩蔽的详细信息。
掩蔽类型
有两种类型的掩蔽:静态掩蔽和动态掩蔽:
- 使用静态掩蔽 (EOAC_STATIC_CLOAKING),服务器将看到来自客户端到服务器的首次调用的线程令牌。 对于首次调用,如果在 CoSetProxyBlanket 调用期间之前设置了代理标识,则使用该代理标识。 但是,如果之前未设置代理标识,则使用线程令牌。 如果不存在线程令牌,则使用进程令牌。 对于所有将来的调用,将使用在首次调用时设置的标识。
- 使用动态掩蔽 (EOAC_DYNAMIC_CLOAKING),每次调用时都将使用当前线程令牌(如果有线程令牌)确定客户端的标识。 如果没有线程令牌,则使用进程令牌。 这意味着,在模拟期间代表客户端调用的服务器会看到发起调用的 COM 客户端的标识,这通常是所需行为。 (当然,为了模拟成功,客户端必须设置适当的模拟级别来授予服务器模拟权限。有关详细信息,请参阅模拟级别。)这种类型的掩蔽成本高昂。
掩蔽如何影响客户端标识
当进行加密调用并且服务器要求客户端提供其标识时,通常会获取绑定到代理的标识。 (有时身份验证服务会根据实际标识执行转换,但通常代理标识是服务器看到的标识。)代理向服务器提供标识,该标识取决于设置的掩蔽类型和其他因素。
总之,客户端的标识是设置的掩蔽标志、进程令牌、线程令牌存在或缺失以及之前是否设置了代理标识的函数。 下表显示了当这些因素有所不同时生成的代理标识(客户端标识)。
掩蔽标志 | 线程令牌存在 | 以前设置的代理标识 | 代理标识(客户端标识) |
---|---|---|---|
未设置掩蔽 |
无关紧要 |
无关紧要 |
进程令牌或身份验证标识 |
EOAC_STATIC_CLOAKING |
显示 |
否 |
线程令牌 |
EOAC_STATIC_CLOAKING |
显示 |
是 |
当前代理标识 |
EOAC_STATIC_CLOAKING |
不存在 |
否 |
进程令牌 |
EOAC_STATIC_CLOAKING |
不存在 |
是 |
当前代理标识 |
EOAC_DYNAMIC_CLOAKING |
显示 |
无关紧要 |
线程令牌 |
EOAC_DYNAMIC_CLOAKING |
不存在 |
无关紧要 |
进程令牌 |
以下流程图说明了在不同情况下如何确定代理标识。
设置掩蔽
在 CoInitializeSecurity 调用中,将掩蔽设置为功能标志,这将为整个进程设置掩蔽。 然后设置掩蔽功能,直到客户端通过调用 IClientSecurity::SetBlanket(或 CoSetProxyBlanket)对其进行更改,这将为代理设置掩蔽。
默认情况下,未设置掩蔽。 若要设置它,请将 EOAC_STATIC_CLOAKING 或 EOAC_DYNAMIC_CLOAKING 传递给 CoInitializeSecurity 或 SetBlanket 中的 pCapabilities 参数。
使用 CoInitializeSecurity 启用静态掩蔽后,首次在代理上调用时,每个代理都会选取令牌(线程或进程)。 当使用 SetBlanket 启用静态掩蔽时,代理会在该时间在线程上选取令牌。 如果在调用 SetBlanket 时没有可用的线程令牌,则进程令牌将用于代理的标识。 实际上,SetBlanket 修复了代理的标识。
使用动态掩蔽时,无论是否使用 CoInitializeSecurity 或 SetBlanket 设置动态掩蔽,都以相同的方式确定代理的标识。 如果存在当前线程令牌,则使用当前线程令牌;否则,将使用进程令牌。
如果通过调用 CoInitializeSecurity 为整个进程设置了掩蔽,并且想要使用进程令牌进行调用,请勿在进行调用时模拟。
掩蔽和模拟级别
如前所述,掩蔽功能确定模拟期间向服务器呈现的标识。 掩蔽为服务器提供了一种方法,用于将自身标识意外的标识投影到它代表客户端调用的另一台服务器。 模拟级别指示客户端为服务器提供多少权限。
模拟而不掩蔽会正常工作,但它可能不是最佳选择,因为在某些情况下,最终服务器需要知道初始调用方的标识。 如果不使用掩蔽,就无法实现这一点,因为很难确保只有经过授权的客户端才能访问远程计算机。 在没有掩蔽的情况下使用模拟时,提供给下游服务器的标识是直接调用进程的标识。
然而,在没有模拟的情况下,掩蔽没有用处。 仅当客户端设置了模拟或委托的模拟级别时,掩蔽才有意义。 (使用较低的模拟级别,服务器无法进行掩蔽的调用。)掩蔽是否成功取决于跨越的计算机边界数和模拟级别,这表示服务器必须代表客户端执行多少权限。
在某些情况下,当客户端将模拟级别设置为 RPC_C_IMP_LEVEL_IMPERSONATE 时,服务器设置掩蔽才有有意义。 但是,某些限制会产生作用。 如果原始客户端将模拟级别设置为 RPC_C_IMP_LEVEL_IMPERSONATE,则中间服务器(充当同一计算机上的客户端)只能跨一个计算机边界掩蔽。 这是因为只能跨一个计算机边界传递模拟级别模拟令牌。 超越计算机边界后,只能访问本地资源。 提供给服务器的标识取决于设置的掩蔽类型。 如果未设置任何掩蔽,则提供给服务器的标识将是立即进行调用的进程的标识。
若要在多个计算机边界掩蔽,必须同时指定适当的掩蔽功能标志和委托级别模拟。 使用此类型的模拟,会将客户端的本地凭据和网络凭据都提供给服务器,因此模拟令牌可以跨越任意数量的计算机边界。 同样,提供给服务器的标识取决于设置的掩蔽类型。 如果未使用委托级别模拟设置任何掩蔽,则提供给服务器的标识是进行调用的进程的标识。
例如,假设进程 A 调用 B,而 B 调用 C。B 已设置掩蔽,并且 A 已将模拟级别设置为模拟。 如果 A、B 和 C 位于同一台计算机上,则将模拟令牌从 A 传递到 B,然后传递给 C 将正常工作。 但是,如果 A 和 C 位于同一台计算机上,而 B 则位于同一计算机上,传递令牌将在 A 和 B 之间正常工作,但不能从 B 传递到 C。从 B 到 C 的调用将失败,因为 B 在掩蔽时无法调用 C。 但是,如果 A 将模拟级别设置为委托,则可以将令牌从 B 传递到 C,并且调用可能会成功。
掩蔽方案
在下图中,未设置掩蔽时进程 A 调用 B、调用 C、调用 D。 因此,每个中间进程都会看到调用它的进程的标识。
使用静态掩蔽时,服务器会看到在从客户端到服务器的首次调用期间设置的代理标识。 下图显示了在从 B 到 C 的调用期间设置的代理标识的示例。在后续调用中,在 B 和 C 设置静态掩蔽时,进程 D 会看到 B 的标识。
使用动态掩蔽时,模拟期间调用方的标识基于当前线程令牌(如果有)。 下图显示了 B 和 C 设置动态掩蔽且 D 看到 A 的标识的情况,但先前从 B 调用 C。