在客户端/服务器场景中,一台服务器调用另一台服务器代表客户端完成某些任务是很常见的。服务器被授权代表客户端行事的情况称为委托。
从安全的角度来看,关于委派会出现两个问题:
- 当代表客户行事时,应该允许服务器做什么?
- 服务器在代表客户端调用其他服务器时提供什么身份?
为了处理这些问题,COM 提供了以下功能。客户端可以设置一个模拟级别,该级别确定服务器能够在多大程度上充当客户端。如果客户端授予服务器足够的权限,则服务器可以冒充(假装是)客户端。在模拟客户端时,服务器只能访问客户端有权使用的对象或资源。充当客户端的服务器也可以启用伪装来掩盖自己的身份,并将客户端的身份投射到对其他 COM 组件的调用中。
考虑上图所示的场景,其中 A 和 B 是与 C 不同的机器上的进程。进程 A 调用 B,B 调用 C。客户端 A 设置模拟级别。B 设置隐身能力。如果 A 设置了允许模拟的模拟级别,则 B 在代表 A 调用 C 时可以模拟 A。呈现给进程 C 的身份将是 A 的身份或 B 的身份,这取决于 B 是否启用了伪装。如果启用了伪装,则呈现给进程 C 的身份将是 A 的身份。如果未启用伪装,则 B 的身份身份将呈现给 C。
如果模拟成功,则说明客户端已经在一定程度上同意让服务器成为客户端。不同程度的模拟称为模拟级别,它们表示在模拟客户端时赋予服务器多少权限。
目前,有四种模拟级别:anonymous、identify、impersonate和delegate。以下列表简要描述了每个模拟级别:
匿名(RPC_C_IMP_LEVEL_ANONYMOUS)
客户端对服务器是匿名的。服务器进程可以模拟客户端,但模拟令牌不包含有关客户端的任何信息。仅本地进程间通信传输支持此级别。所有其他传输都默默地提升这个级别来识别。
识别(RPC_C_IMP_LEVEL_IDENTIFY)
系统默认级别。服务器可以获取客户端的身份,服务器可以冒充客户端做ACL检查。
模拟(RPC_C_IMP_LEVEL_IMPERSONATE)
服务器可以在代表客户端执行操作时模拟客户端的安全上下文。服务器可以作为客户端访问本地资源。如果服务器是本地的,它可以作为客户端访问网络资源。如果服务器是远程的,它只能访问与服务器在同一台计算机上的资源。
委托(RPC_C_IMP_LEVEL_DELEGATE)
最强大的模拟级别。选择此级别时,服务器(无论是本地的还是远程的)可以在代表客户端执行操作时模拟客户端的安全上下文。在模拟期间,客户端的凭据(本地和网络)可以传递到任意数量的计算机。
要在代理级别进行模拟工作,必须满足以下要求:
- 客户端必须将模拟级别设置为 RPC_C_IMP_LEVEL_DELEGATE。
- 客户帐户不得在 Active Directory 服务中标记为“帐户敏感且无法委派”。
- 服务器帐户必须在 Active Directory 服务中标记为“信任委派”属性。
- 托管客户端、服务器和任何“下游”服务器的计算机都必须在域中运行。
通过选择模拟级别,客户端告诉服务器它可以模拟客户端多远。客户端在它用来与服务器通信的代理上设置模拟级别。
设置模拟级别
有两种方法可以设置模拟级别:
- 客户端可以通过调用CoInitializeSecurity来设置它的进程范围。
- 客户端可以通过调用IClientSecurity::SetBlanket(或辅助函数CoSetProxyBlanket)在远程对象的接口上设置代理级别的安全性。
您可以通过dwImpLevel参数将适当的 RPC_C_IMP_LEVEL_xxx 值传递给CoInitializeSecurity或CoSetProxyBlanket来设置模拟级别。
不同的身份验证服务在不同程度上支持代理级别的模拟。例如,NTLMSSP 支持跨线程和跨进程委托级别的模拟,但不支持跨计算机。另一方面,Kerberos 协议支持跨计算机边界的代理级别模拟,而 Schannel 不支持代理级别的任何模拟。如果您有一个模拟级别的代理,并且您想将模拟级别设置为委托,您应该使用除模拟级别之外的每个参数的默认常量调用SetBlanket 。COM 将在本地选择 NTLM 并在远程选择 Kerberos 协议(当 Kerberos 协议工作时)。