下载本文的代码:SecurityBriefs0511.exe
(116KB)
最近,我花了很多时间来使用 Microsoft® .NET
Framework 2.0 构建安全的 Web 服务,Web Services
Enhancements (WSE) 3.0
成了我的救星,因此我想应当针对此新产品中的安全性功能开辟一个专栏。这是围绕
Visual Studio® 2005 构建的第一个版本,而 WSE 3.0
事实上并不会在 1.1 版 .NET Framework
上运行,因此所有这些信息还有点像“打前锋”,在最终版本问世之前会随时更改。对于本专栏,我使用的是
Beta 版 WSE 3.0。
本页内容
认识 WSE 3.0
如果您曾用过先前版本的 WSE,那么您一定会很欣赏直接构建到 Visual Studio 2005 中的 WSE 设置工具,因为它可以轻而易举地创建安全策略。虽然这与旧版本的
WSE 类似,但变化还是很多的。一整套的交钥匙安全策略声明使 Web 服务的安全保护减少了许多主观判断工作。如果您曾读过我以前关于“用户名”令牌的文章
[请参阅使用 WSE 2.0 保证用户名令牌的安全(英文)],那么您一定深深体会到要使用 WS-Security 构建无懈可击的验证系统是多么的困难。使用交钥匙策略声明将有助于避免该篇文章中提到的许多问题。
WSE 3.0 中有状态的安全环境令牌 (SCT)
大大简化了面向会话的系统的构建工作。您只需切换
Web.config 文件中的开关,便可以指示 WSE 在其 SCT
中保持足够的状态,来与远程 Web 服务重新建立 WS-SecureConversation
会话。有状态的 SCT
提供了确保可靠性的措施(如果会话中断,可以透明地重新启动)并可以跨
Web 领域支持会话。
在大多数 Web
服务部署中,使用策略文件来描述安全性要求是一种非常好的方式,管理员因此而有了所需的灵活性,即使服务的安全环境发生变化,也可以确保
Web
服务的可访问性和安全性。但在有些情况下,您可能不希望这样做,而是希望配置选项只满足支持应用程序所需的最基本要求。
在先前版本的 WSE
中,用于指定安全性配置的策略模型与基于代码的模型是分开的。也就是说,如果您不想使用
WSE 定义的策略文件,就必须自己将 WS-Security
标头拼凑起来。而在 WSE 3.0
中,可以以代码的形式创建策略声明,提供的参数与通过策略文件提供的参数相同。这真是一项重要的功能,尤其在某些新的交钥匙声明的实现非常复杂时则更是如此。您一定不会希望手工进行处理!
最后,我最喜欢的一项功能就是用于构建自定义策略声明的清晰的新模型。这种方式非常适合将自定义的授权添加到
Web 服务。在 Web Services Enhancements 3.0
中这种方式简化了许多。
交钥匙安全声明
交钥匙安全声明是最重要的新增安全性功能,因此我首先就此进行着重讨论。WSE
团队花费大量时间开发了多个用于确保 Web
服务安全性的策略。他们主要考虑使用 X.509
证书通过安全套接字层 (SSL) 或 WS-Security
进行服务器验证、通过证书或用户名令牌进行客户机验证、以及其他更特殊的情况(例如使用
Kerberos 进行相互验证和通道安全)。
在我列举每个交钥匙声明所使用的策略之前,我想先指出各策略的共同之处:每个策略均设法提供验证、消息完整性和保密性。但每个声明以不同的方法实现这些安全目标。
在 June CTP 中共有六种交钥匙声明。第一种是
AnonymousOverCertificateSecurity,其中 Web 服务通过其 X.509
证书对其本身进行到调用者的验证。所有调用者均是匿名的。通过使用服务器公钥交换的对称密钥提供消息级的完整性和保密性。在逻辑上,此模型与您访问喜欢的网上书店并购买某本书时所发生的情况类似。书店的客户太多了,不可能对每个客户逐一验证,但接受信用卡号和帐单接收地址是很好的方法,这样这笔交易的验证责任就转给银行了。
在许多企业对企业的 Web
服务情况中,客户机和服务器都分配有证书。这种情况下,您可以从以下两种声明选择一种:CertificateMutualAuthenticationProfile
或 MutualCertificateSecurity。它们在逻辑上非常相似,客户机和服务器通过彼此出示
X.509
证书进行验证,并证明相应私钥的所有权。通过使用服务器公钥交换的对称密钥提供消息级的完整性和保密性。第一种声明依赖于现有的
WS-Security 1.0 规范,而第二种则使用 WS-Security 1.1
规范草案,其中包括一些新增功能(例如加密的 SOAP
标头)。
虽然使用证书对客户机进行验证是个不错的选择,通常这样做简单但却不可行,因为这增加了维护(发放和管理客户机证书所必需的)公钥基础结构
(PKI)
的开销。通常,通过简单的用户名和密码对客户机进行验证更加合理。以下是支持此类验证的两种交钥匙声明:UsernameOverTransportSecurity
和 UsernameOverCertificateSecurity。如果要依赖 SSL
验证服务器并为通道提供完整性和保密性,则应当使用
UsernameOverTransportSecurity。而当 SSL
不适用时(例如,当有中间方并且要进行端对端验证时),UsernameOverCertificateSecurity
则十分有用。
最后,如果要为支持 Kerberos 的 intranet 构建 Web
服务(现代 Windows® 域环境中的情况),KerberosSecurity
声明是非常合适的选择。由于 Kerberos
使用纯传统的加密方法来验证客户机和服务,因此与基于证书的解决方案相比,它不会为服务带来那么大的负担。但迄今为止
Windows
域环境的最大好处是支持单一登录。通过使用客户机的默认登录凭据进行验证,无需查询客户机密码。当然也不需要
PKI。服务器可以使用 Windows
组作为角色为资源访问授权或者只是模拟客户机,并让
Windows 处理访问检查。
通过策略使用交钥匙声明
通过 WSE
设置工具,可以轻松地以一种交钥匙声明创建安全策略。如果安装了带有
Visual Developer 选项的 WSE 3.0,则当您右键单击 Solution
Explorer(解决方案资源管理器)中的某个项目时,上下文菜单的底部将出现
WSE Settings 3.0 选项。选择该选项后,即可为 WSE 启用
Web 服务项目。在 Policy(策略)选项卡上,可以使用安全性设置向导添加新策略。每个策略有一个名称,可用来在代码中引用策略。添加新策略时,将很快进入如图
1
所示的页面。在此选择的验证方法将决定最终使用哪个安全策略声明。
图 1 选择验证方法
一开始,此对话框中的用语可能会使您迷惑,但当您选择某个验证方法时,您实际上是要决定客户机的验证方式。因此如果您想使用上述的
AnonymousOverCertificateSecurity 声明,您应选择 Anonymous(匿名)选项。与此类似,若要使用
KerberosSecurity,您应选择 Windows 选项。Certificate(证书)选项会带给您两个基于证书的相互验证声明,不过最终的选择将取决于您是否选择使用
WS-Security 1.1
扩展,有关它的复选框会随后在向导中出现。Username(用户名)选项会带给您两个“用户名”声明之一,最终的选择将是传输安全性或者消息级安全性,它们不会同时出现但显示的位置相同:如果选择启用
WS-Security 1.1 扩展,则您最终将使用
UsernameOverCertificateSecurity 声明,否则将使用
UsernameOverTransportSecurity。查看此向导中的摘要页面可以确认是否有所需的声明类。图
2 显示了一个示例。
图 2 摘要页
究竟是在策略中指定凭据还是通过代码指定凭据,您时常会面临这样的选择,这也取决于您是要保护的服务应用程序还是客户端应用程序。通过策略指定服务证书是最方便的,您只需确保将证书安装在将部署客户机或服务的机器上即可。另一方面,用于识别客户机的用户名和密码应通过调用
SetClientCredential
以代码形式动态地设置。在配置文件中设置明文密码是一个非常糟糕的办法。
WSE 策略文件仅仅是已命名策略的一个容器而已。建立自己的策略后,您需要告诉 WSE 该使用哪个。对于服务,这简单到只需将 PolicyAttribute
添加到实现该服务的类。而对于客户机,我通常只是调用 SetPolicy 来选择要使用的策略,尽管您想要给朋友留下深刻印象,也请您在代理上使用局部类添加
PolicyAttribute。图 3 说明我多么倾向于设置策略。请注意,您需要一条针对 Microsoft.Web.Services3
命名空间的 using 语句,使编译器能够接受此示例代码。

图 3
通过代码使用交钥匙声明
我们发现,当 WSE
从策略文件初始化其安全性设置时,实际上会创建一个代表策略声明的类的实例,然后让该实例从策略文件的
XML
中初始化其设置。一旦知道了这一点,就可以很容易地跳过这一步骤,而只是在代码中动态地创建策略。通过这项技术,您可以选择管理员在部署时要控制哪些设置以及哪些设置需要硬编码。当然,如果硬编码的配置过多,无异于自找麻烦,因此这时候要格外小心。
WSE
策略是策略声明的有序集合。当存在多个声明时,顺序是非常重要的,这一点在后面讨论自定义策略声明时将会谈及。但是现在,我希望简单一些,我要告诉您如何使用单一的交钥匙安全声明来动态地创建策略。
第一步是创建并配置交钥匙声明。我在下面列出代表这些声明的类。它们与我先前介绍的六种交钥匙声明相对应:
• |
AnonymousOverCertificateAssertion
|
• |
CertificateMutualAuthenticationProfileAssertion
|
• |
MutualCertificateAssertion
|
• |
UsernameOverTransportAssertion
|
• |
UsernameOverCertificateAssertion
|
• |
KerberosAssertion
|
假设您要使用常用的 UsernameOverCertificateAssertion。图 4 显示一个可使用向导创建的
XML 策略。最好学会对照声明编写代码,因为您见到的属性都直接对应于声明类的所有属性。图 5 显示您应如何以代码形式动态地构建等效的声明。如果并排查看这两幅图,您就会看到它们之间的转化有多么容易。

图 4

图 5
既然已经获得了策略声明,您就需要将它封装到策略对象中,以供
WSE 使用。创建策略是非常简单的事 - 只需这样:
//创建策略,然后添加到
//声明
Policy policy = new Policy();
Policy.Assertions.Add(a);
最后一步就是将策略绑定到服务或客户端代理。要将其绑定到代理客户端,只需调用 SetPolicy(如图 3 所示),传递策略对象而不是字符串。SetPolicy
被重载,以接受代表 XML 策略文件中命名策略的字符串或动态构建的策略对象。
绑定服务则有所不同。您需要创建一个派生自 WSE
类的新类,使用构造函数构建声明,然后将其添加到声明集中。下面是一个示例:
public class MyServerSecurityPolicy :Policy {
public Policy() {
... //在此构建声明
Assertions.Add(a);
}
}
现在,您就可以像刚才那样使用 PolicyAttribute
来指示将哪种策略用于服务,就是在此时,您将策略指定为类型,而不是字符串:
[Policy(typeof(MyServerSecurityPolicy))]
public class MyService :WebService {
[WebMethod]
public void DoSomething() {
...
}
}
自定义策略声明
如上所述,策略仅仅是策略声明的有序集合。实际上,当
WSE
在运行期间初始化服务或代理时,这些策略声明被实例化。然后每个声明被要求提供代码,以在
WSE
消息处理管道中实现其特定的策略。因此策略声明就相当于模板,用于安排将处理每条消息的管道,策略将决定各步骤的执行顺序。
在我撰写 WSE
安全性动手体验训练材料时,我构建了一个日志记录安全声明,它可以放在管道中的任何位置。例如,如果要看到外发消息在安全性声明实施加密前的样子,您应对策略进行排序,使日志记录声明位于交钥匙安全声明之前。哦,如果您还想看到其加密后的样子,可以将日志记录声明的另一个实例放在安全声明之后,并将其指向另一个文件。策略的
XML 可能如下(我省略了安全策略声明的细节):
<policy name="MyServicePolicy">
<myTrace output="beforeSecurity.xml"/>
<usernameOverX509Security ... />
<myTrace output="afterSecurity.xml"/>
</policy>
既然 WSE 不知道 <myTrace>
元素的作用,我就必须将我为处理日志记录而编写的相应策略声明类的名称告诉给它:
<extensions>
<extension name="myTrace" type="MyAssertion, MyAssembly"/>
<!-- omitted the standard WSE entries -->
</extensions>
这条简单的声明不但非常有助于使用 WSE 3.0 调试
Web
服务,而且也有着很好的示范作用,可以帮助您试着编写自己自定义的声明。
那么为什么我要在安全性的专栏中不厌其烦地谈论这项功能呢?要使公共代码独立于一个甚至一组
Web
服务,策略声明是一种很好的方式。对于那些可能忘记了亲自进行安全性检查的应用程序开发人员来说,在消息真正到达
Web 方法之前在 WSE
管道中执行安全性检查是一种简单方便、大有助益的方式。
与此类似,还有许多与安全性相关的功能可以提升至管道中。例如,如果客户端使用证书进行到服务的验证,您可以将这些证书映射到真正的
Windows 用户帐户,以便可以使用 Kerberos
凭据向后端服务器发出请求。基于角色的访问检查也是其中一例。任何可以提升至管道中的安全功能都有助于确保跨所有
Web 方法应用一致的安全策略。
请求的验证又怎样呢?构建安全系统时,验证用户输入数据是一个重要的步骤。如果可以某些功能提升至管道中,对您将大有助益。Aaron
Skonnard 曾针对 WSE 2.0 的此方面问题做过研究,如果您有兴趣看一个示例,请阅读 2004 年 3 月号“XML
文件”专栏的 WS-Policy 与 WSE 2.0 声明处理程序(英文)。如果您想构建自己的自定义策略声明,就请以我的追踪声明为起点开始您的工作,但我还是要给一些提示,帮助您开展工作。
每个策略声明均有机会将筛选器对象插入实际实现此策略声明的管道中。筛选器体系结构非常简单。您必须从
SoapFilter 中派生一个类并重写一个方法:ProcessMessage。以下是一个筛选器示例,它只是让消息流过而不执行任何操作:
class NoOpFilter :SoapFilter {
public override SoapFilterResult ProcessMessage(
SoapEnvelope envelope) {
return SoapFilterResult.Continue;
}
}
WSE 中有四个独立的管道:
• |
发送者的输出管道
|
• |
接收者的输入管道
|
• |
接收者的输出管道
|
• |
发送者的输入管道
|
请注意,上面的管道排列顺序遵循的是请求/响应消息对从发送者流向接收者再返回发送者这样一个自然流动顺序。由于策略声明的行为在上面四个点可能要有所不同,因此要求策略声明提供针对这四个位置的筛选器。事实上,您可以重写策略声明中的四种方法来提供以下筛选器:
• |
CreateClientOutputFilter
|
• |
CreateServiceInputFilter
|
• |
CreateServiceOutputFilter
|
• |
CreateClientInputFilter
|
请注意,当策略声明仅在服务进程中运行时,如果您被要求为客户端管道创建筛选器,那么只需抛出一个异常即可,因为永远也不会为服务管道调用客户端函数。这同样也适用于客户端声明。
在我的日志记录策略声明中,四种筛选器执行完全一样的操作:将当前消息记入命名的平面文件。因此在我的策略声明中,我只是为所有四种方法分发了同一筛选器类的实例。此外,我重写了策略声明的
ReadXml 方法,以便可以在 XML
策略文件中为基于属性的每个筛选器配置文件名。将此值传递给要创建的筛选器。
在用多个声明构建策略时,事情开始变得饶有趣味。请记住,策略决定着管道中的处理顺序。假设您编写以下策略并将其同时用于客户端和服务:
<policy name="MyPolicy">
<assertionOne/>
<assertionTwo/>
</policy>
图 6 将声明映射到筛选器
由于 assertionOne
列在前面,所以它先被要求插入其筛选器。然后
assertionTwo
插入其筛选器。结果,客户端和服务的管道将如图 6
所示进行排序,即遵循图中典型请求/响应消息对的处理顺序。
在此讲一下我是如何记住这个顺序的,这就相当于程序和线路之间的消息流。设想程序位于策略顶部,而线路位于底部。在客户机和服务器端都是这样的,这样可以在客户机和服务器之间共享策略文件,而不必重新排序。
状态管理
有时候,您想使筛选器始终有状态,无论是在单个请求/响应对期间还是跨多个不同的请求期间都是如此。为此,WSE
提供了三种状态包。MessageState
允许在消息流过单个管道期间共享状态。OperationState
针对构成请求/响应对的消息而设计:您可以在请求期间写入
OperationState,而在响应期间将其读回。最后,通过基于每个代理进行管理的
SessionState 跨各请求存储状态。
您可以通过 SoapContext
类的属性获得这些状态包,而前者本身则可以通过
SoapEnvelopef 类的 Context
属性获得。如果回头看看我的筛选器示例,您会发现
SoapEnvelope
被传递给了筛选器,因此这些状态包可以轻松地被访问。
小结
开发日志记录声明时,我对 WSE 3.0
管道体系结构所表现出的逻辑印象深刻。即使没有说明文件,我也可以在
30
分钟内将一个简单的日志记录声明完成并投入运行。这都得益于
WSE 团队的出色工作!我的亲身体验证明,WSE 3.0
功能将受到初学者和高级开发人员的赏识。于我而言,自定义策略声明体系结构让我可以灵活地构建安全管道,使开发人员可以在其应用程序中通过策略轻松地反复使用。如果您刚刚接触
Web 服务,WSE 3.0
中的交钥匙安全策略声明将帮助您使用最佳的实践经验构建系统并避免
Web 服务安全性中内在的难以发现的缺陷。由于 WSE
3.0 与 Windows Communication Foundation (WCF)(以前名为
"Indigo")线路兼容,因此您还会为 WCF
做好准备。
|