代码改变世界

WCF 第八章 安全 确定替代身份(下)-模仿用户

2011-01-26 12:04  DanielWise  阅读(714)  评论(1编辑  收藏  举报

默认情况下,WCF服务使用宿主服务执行时的身份信息来访问本地和远程资源。由服务负责认证调用方来确定它们是谁,然后执行授权检查来保证它们可以访问其他资源(将使用本地身份来访问)。当运行接收Windows权限信息的服务时,我们有其他选择,比如模拟用户。

  身份模拟是使用另外的权限/身份来执行程序逻辑的过程。一个服务可以通过假定调用者的身份来模拟用户。这是一个单次调用的典型周期,但是身份模拟令牌可以被服务保留并重用。调用方所在的线程与模拟身份关联,操作在假定的身份的授权和角色下执行。

  因为身份模拟是重要的,通过采用一个调用者的身份,服务仅可以访问调用者有权限访问的资源。通过在调用者的权限下运行,很容易保证只有那个用户的相关数据和资源可以被访问。

  身份模拟是服务和它的调用者之间的一个协议。更高级别的身份模拟要求客户端和宿主机器的系统都要有权限(在一些情况下)。在开始之前,让我们配置一下服务端代码使其支持身份模拟。这是通过OperationBehaviorAttribute完成的,它有一个Impersionation 参数。这个参数来自ImpersonationOption枚举的一个选项。具体例子请查看列表8.24.

列表8.24 通过OperationBehaviorAttribute要求模拟

        [OperationBehavior(Impersonation=ImpersonationOption.Required)]
        public string GetSecretCode()
        {
            DisplaySecurityDetails();
            return "The Secret Code";
        }

  ImpersonationOption可以是NotAllowed,这个选项禁用身份模拟,Required,这个选项要求客户端同意被模拟身份,Allowed,这个选项的意思是如果客户端同意模拟身份就使用,反之则不使用身份模拟,尽管通常不会这么做而且应该尽量避免。

  在所有必要的操作上为身份模拟配置OperationBehavior是可能的,但是你可能也要在配置文件中为所有操作启用身份模拟。列表8.25显示了如何使用ImpersionateCallerForAllOperations选项,这个值默认是false.

列表8.25 通过ImpersionateCallerForAllOperations开启身份模拟功能

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <behaviors>
            <serviceBehaviors>
                <behavior name="ServiceBehavior">
                    <serviceAuthorization principalPermissionMode="UseWindowsGroups"
                        roleProviderName="" impersonateCallerForAllOperations="true" />
                    <serviceMetadata httpGetEnabled="true" />
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <services>
            <service behaviorConfiguration="ServiceBehavior" name="SampleService.Samples">
                <endpoint address="" binding="netTcpBinding" bindingConfiguration=""
                    name="netTcp" contract="SampleService.ISamples" />
                <endpoint address="mex" binding="mexHttpBinding" bindingConfiguration=""
                    name="mex" contract="IMetadataExchange" />
                <host>
                    <baseAddresses>
                        <add baseAddress="http://localhost:8080/Samples" />
                        <add baseAddress="net.tcp://localhost:8090/Samples" />
                    </baseAddresses>
                </host>
            </service>
        </services>
    </system.serviceModel>
</configuration>

提示 通过代码模拟身份

通过代码手动调用身份模拟功能也是可以的。WindowsIdentity通过ServiceSecurityContext.Current暴露,它有一个可以通过调用来激活身份模拟的Impersonate方法。在你第一次尝试调用之前确保WindowsIdentity 不是空值。

  接下来,客户端(在一些情况,完全模拟或者委托是必要的)必须显式指明它支持身份模拟。这可以通过配置文件或者代码实现。对配置文件来说,需要保证客户端的配置与列表8.26中显示的类似。

列表8.26 在客户端配置文件中确定SupportedImpersonation 级别

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <behaviors>
            <endpointBehaviors>
                <behavior name="EndBehave">
                    <clientCredentials>
                        <windows allowedImpersonationLevel="Impersonation" />
                    </clientCredentials>
                </behavior>
            </endpointBehaviors>
        </behaviors>
        <bindings>
            <netTcpBinding>
                <binding name="netTcp" closeTimeout="00:01:00" openTimeout="00:01:00"
                    receiveTimeout="00:10:00" sendTimeout="00:01:00" transactionFlow="false"
                    transferMode="Buffered" transactionProtocol="OleTransactions"
                    hostNameComparisonMode="StrongWildcard" listenBacklog="10"
                    maxBufferPoolSize="524288" maxBufferSize="65536" maxConnections="10"
                    maxReceivedMessageSize="65536">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <reliableSession ordered="true" inactivityTimeout="00:10:00"
                        enabled="false" />
                    <security mode="Transport">
                        <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
                        <message clientCredentialType="Windows" />
                    </security>
                </binding>
            </netTcpBinding>
        </bindings>
        <client>
            <endpoint address="net.tcp://localhost:8090/Samples" behaviorConfiguration="EndBehave"
                binding="netTcpBinding" bindingConfiguration="netTcp" contract="Samples.ISamples"
                name="netTcp">
                <identity>
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

  你也可以通过客户端代码的服务代理确定一个特殊的模拟层次,就像在列表8.27中显示的那样。

列表8.27 通过客户端代理确定身份模拟

  Samples.SamplesClient proxy = new Client.Samples.SamplesClient("netTcp");
            proxy.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Delegation;

  无论AllowedImpersionationLevel属性是否在配置文件或代码中设置,都支持从下面TokenImpersionationLevel枚举中的选项:

  None. 不执行身份模拟。

  Anonymous. 身份模拟用来进行权限检查,但是服务端代码不知道调用方是谁。仅可以用在在同一台机器上使用的绑

  定,如NetNamedPipeBinding.

  Identity. 不执行身份模拟,但是服务端代码知道调用方是谁而且基于那个身份可以做出访问决定。

  Impersionate. 服务可以确定调用者身份,就像使用Identity时的配置,但是在这个模式,可以在同样的机器上用来对资

  源进行身份模拟。

  Delegate. 与Impersionate一样;然而,证书可以用于基于网络的资源访问。

  当启用身份模拟时要小心使用,要考虑到全部系统的一部分被攻击影响后的后果。例如,如果你开启了委托(在这个情况下通过配置文件和活动目录权限,默认情况下将不会允许这样做)而且一个有域管理员权限的用户调用了你的服务(通过一个已经选择开启委托的客户端),你的服务逻辑应该折中吗?这些管理员身份有很高的权限可以用来访问域中任意资源。很明显,风险也是很高的,你应该花费时间来完全理解模拟身份选项和通过早先介绍的PrincipalPermissionAttribute来拒绝权限的能力。

  如果你了解了风险,身份模拟仍然是一个强大的思想,可以用来根据调用者提供给服务的权限高效地管理资源访问。