Start from here!

从这里开始...

博客园 首页 新随笔 联系 订阅 管理

.NET Web Services提供了大量的认证和授权选项来保护你的商业数据。
在讨论.NET Web Services时,安全就变成了其中的一个最大问题。当你暴露商业应用程序的一部分作为Web Services时,你要确信不会危及到你的商业数据的安全。同样地,当编写一个依靠Web Services的商业应用程序时,你要确信你传到Web Services或者从Web Services来的数据不会无故地被暴露。我将来回顾一下Web Services安全的一些选项,对你可能遇到的一些情况提供一些建议。

首先,你必须熟悉两个术语—认证(authentication)和授权(Authorization)。如果把Web Service比作一个建筑物,authentication是一个保安,当人们进入时检查每个人的证件,对没有证件的人进行阻拦。相似地,为了调用一个Web Service,客户端必须认证。Authorization-或者访问控制,相当于每个锁着的门。为了进入这个房间,你必须有这个房间的钥匙。相似的,为了使用你的Web Service的功能,客户必须被授权使用。我将集中给大家说说作为安全关键领域的认证。

.NET Web Services和任何Web应用程序是相似的,他们包含两个普通的安全执行目录:系统和应用程序。在系统安全方面,程序依赖于平台服务来提供需要的认证和授权。在应用程序安全方面,应用程序执行所有必须的代码来认证和授权用户。系统安全的主要优点是你不需要自己写代码和测试代码。IIS提供了适合许多情况的各种认证方法(见资源中的IIS的认证特征列表)。然而用系统安全,你被绑定到平台安全服务上,并受到平台安全服务的限制。应用程序安全服务给你更多的灵活性,当然,代价是你自己建立所有的东西。

你将发现一个决策图表帮助你选择你的认证机制(见图1)。如果你建立了一个局域网内使用的Web Service,你选择基于局域网的拓扑图。如果Web Service使用者和Web Service在同一个域中,或者在被Web Service信任的域中,你可以使用集成的Windows鉴定。使用这种整合安全机制,你让Windows使用当前用户登陆的信任书。这对最终用户是方便的,因为用户登陆到Windows工作站,用户的身份就被自动传递给你的应用程序。

如果用户在不信任的域中,你需要决定是用Windows帐号还是用你自己的应用程序建立和管理帐号。如果你使用Windows帐号,建立和维护用户的帐号的工具是免费的。然而,用户最终还是要使用IIS登陆到Windows服务器或者Windows域中存取你的应用程序。


				图1.
图1. 使用决策图表
而且,依赖Web server上的本地用户帐号,使得扩展到更多的Web servers变得麻烦了。你必须把这些用户帐号复制到你安装的每个新的Web servers。基于应用程序的帐号给你更多的灵活性,更容易扩展,因为你可以把用户帐号保存在一个中心数据库上,可以被所有的Web servers所访问。但是你写所有的需要的工具来建立和维护用户帐号。

决定让Windows管理用户帐号提供了两个可能的认证方案。如果你在使用Windows 2000域控制器,客户端都使用IE5或者更高的版本,你可以使用Digest认证。Digest认证是一个Internet Engineering Task Force (IETF)标准认证机制,在这个机制中,客户端(IE5)在把用户的信任书发送到服务器(IIS5)之前对他们进行加密。

Digest认证并不能满足所有的要求,你的另外的选择就是Basic认证。Basic认证是一个老的IETF标准,在这个标准中,客户用清晰的文本传递用户的信任书到Web server。因为Basic认证用清晰的文本传递密码,它通常和Secure Sockets Layer(SSL)一起使用来加密在客户端和服务器端之间传递的数据,以防止信任书被中途截获。然而,如果你在局域网中使用Basic认证,你可以决定不去使用SSL来避免这种截获。

建立因特网服务
如果你正在建立一个在因特网上使用的Web service,你的选择和在局域网中被不信任的域使用的情况是相似的。主要的不同是你决定用Basic认证还是用应用程序认证,你很可能需要使用SSL来加密在Web上使用的信任书。我们根据用户密码被盗后所带来问题的严重性,来做出决定。比如,如果你认证用户仅仅使用一些个性化的内容,比如用户所在城市的天气状况,别人几乎没有什么动机来偷这个密码。然而,如果你认证用户使其进入你的用来管理证券投资的股票交易程序,你最好使用SSL。

为了在.NET Web service中使用Integrated、Digest、或者Basic认证,你必须像一般的网络应用程序一样配置 IIS。此外,你必须配置ASP.NET应用程序,因为.NET Web service在ASP.NET Framework的上层,而.NET Framework在IIS的上层。为了能够正常的工作,认证必须在两个地方都配置(见图2)。

商业应用
确保你的.NET应用程序投资是安全的
Web Services和.NET带来了新的,引人注目的程序应用机遇,也带来了重大的,吸引人的商业时机,它们回报了你所做的投资,所有权的总成本和项目管理模式。然而,如果没有正确的安全策略...
MSDN主题“ASP.NET数据流”(见资源)讲述了IIS和ASP.NET在两种常见的情况下,如何一起工作来把安全机制应用到ASP.NET应用程序中。你使用Web.comfig来配置ASP.NET的认证。Visual Studio .NET把这个XML文件放在包含Web Service的应用程序的根目录下。你可以设置认证元素的mode特性为下面的四个值之一:None, Windows, Forms, 或者Passport。比如,下面的代码中,“None”关闭ASP.NET的安全认证:
<authentication mode="None" />

记住,无论如何,IIS的安全必须配置为允许匿名访问。否则用户被IIS认证,但是ASP.NET应用程序自己认为用户没有得到认证。把认证的mode设置为Windows,意味着应用程序将依靠IIS来执行认证,这和我前面提到的系统安全相一致。如果你给IIS配置了除匿名以外的权限,这就是你所要实现的。而且,设置本身并不能确定用户是否会被认证,IIS必须设置除匿名之外的一些权限。

Forms认证—过去被叫做cookie认证—实际上是系统和应用程序认证的混合体。它依赖于用户使用HTML表单输入userID和 password。你写代码来认证用户,很可能是从应用程序的用户数据库中读出数据来认证。一旦用户得到认证,一个唯一的key被送到客户端存为cookie。客户端在随后的请求中把cookie发回服务端,服务端在处理请求之前先检查这个cookie。这种认证对Web Service来说是不合适的,因为一个典型的Web Service客户端不知道如何处理登陆表单。如果你正在给一个Web Service写一个客户端代理,你很可能使用Forms认证,只要你提交userID, password登陆表单,它就会处理cookie。

Forms方法的一个缺点就是userID, password, 和 cookie这些信息都是用清晰的文本来传递的,很有可能被怀有恶意的黑客所截获。最后,Passport认证使用微软的Passport服务。Passport象一个Windows为web创建的整合的安全机制。它可以让用户在一个Web session过程中登陆一次,就可以从一个使用Passport的网站到另一个使用Passport的网站,而不需要每个站点都再次登陆。Passport认证也是基于Forms的:用户必须在一个HTML表单中录入一个userID 和password,把这个表单提交给Passport服务器。

如果你有一个Passport登陆,你可以使用一个在www.passport.com/sdkdocuments/sdk21/default.htm 上的Passport SDK来把用户的信任书发到Passport服务。在Web Services中使用Passport认证并不可行,除非你的Web service客户端知道如何处理Passport登陆要求。

如果你依赖 IIS做认证,Web Service客户端可能把用户的信任书作为HTTP要求的一部分发到你的服务。如果客户端是.NET客户端的话,这个过程将会非常的容易。你建立一个新的NetworkCredentials对象,把userID 和password传递给它的构造器。然后你可以使用NetworkCredentials对象来设置Web Service代理的Credentials属性。下面是一个用户使用Web Service的例子。这个Web Service的名字叫Invoicing。

Dim inv As New com.devxpert.Invoicing()
inv.Credentials = New NetworkCredential("AppUser", "password")
'the above credentials are
'passed to the Web server
'with this method call
inv.DoWork()

 

 

实现应用程序安全
就象你收集的信息显示的一样,每个基于 IIS的认证方法都有限制。因为这些限制,加上应用程序的特别要求,根据我的经验,你在可能的情况下最好让你的应用程序处理用户认证。

不管你如何实现你的应用程序认证,从本质上结构是一样的。首先,客户端发送应用程序用来鉴定用户的userID 和 password。比如,你的应用程序也许在数据表中搜索提供的userID,然后比较数据库中的密码和客户端发过来的密码。

如果userID 和password是正确的,应用程序将会产生一个key—一个单独的有次序的位串—把它发到客户端同时存到key缓存中。客户端必须把这个key送到应用程序的每个请求中去。随着每个请求,应用程序在Key 缓存中检查提供的每个key,基于这个检查来决定用户是否得到认证。

当实现这个架构的时候你必须做几个决定。首先,你必须决定在线传递时如何对客户端的信任书和key进行加密。如果你在执行一个定制的Web Service客户端,你可以在把他们发往服务端之前在客户端加密敏感信息。然而,为了正确地做到这点,你会发现你又遇到了其它问题,比如,如何确保加密的key在客户端和服务端之间同步的,如何防止key在客户端的不安全。不要采用你自己的加密,取而代之的是,应该考虑使用SSL来加密所有的客户端和服务器端的交互信息。尽管使用SSL将会卷入前面提到的性能问题,但是它对于大多数在客户端和服务器端交互的非公有的商业应用程序,SSL是更合适的。

在多数情况下,你将在你的Web Service上建立一个方法,它能够让客户端登录到这项服务上去。

<WebMethod()> 
Public Function Logon(ByVal userId As String, ByVal _ 
pwd As String) As String
 Dim authKey As String
 If SecurityMgr.Logon(userId, pwd, authKey) Then
   Return authKey
 Else
   Throw New Exception("Invalid user name or password")
 End If
End Function

这个方法检查用户名数据表中的userID 和password。如果他们有效,它建立一个随机key,把它存到名字叫AuthKeys的数据表中。这个方法也把当时的日期和时间存在字段LastUsed中去。后面要用到LastUsed字段来确定是否这个key终止。

接着,决定如何把key送到接下来的每个请求中。你可以在每个Web Service方法里增加一个字符串参数,要求客户端发送这个参数。我更喜欢使用SOAP header。SOAP header是一个可以扩展的机制,它可以让你往SOAP协议上增加功能。.NET使得定义和使用SOAP header非常的简单。相似的,用SOAP Toolkit 2.0处理SOAP headers也是很容易的。如果你的客户端使用其它的SOAP工具,首先看看从那些客户端发送SOAP headers是多么的容易。

使用SOAP headers,你首先要建立一个从SoapHeader继承来的类和一个在报头需要的公共方法。

Public Class authHeader Inherits SoapHeader
 Public authToken As String
End Class

然后你可以建立一个这个类的实例,把它作为你的Web Service类的一个成员变量:

<WebService(Namespace:="http://devxpert.com/nwind.net")> _
Public Class Invoicing Inherits System.Web.Services.WebService
'the auth SOAP header
Public authHdr As authHeader

现在这儿有个有趣的部分。每个Web Service方法都需要报头—在这个例子中所有的方法都需要报头,因为它包含认证key—你可以增加SoapHeader特性:

<WebMethod(), SoapHeaderAttribute("authHdr", Direction:= _ 
SoapHeaderDirection.In,Required:=True)> _ 
Public Function SubmitInvoice(ByVal invoiceDoc As String) As Integer

.NET把报头从SOAP请求中分析出来,把它移到authHdr的authToken成员上。你的代码通过用authHdr.authToken来检查它是否是一个有效的key。你可以写一个存储过程来检查AuthKeys表中的key,如果找到key,更新LastUsed字段来反映当前的日期和时间。

在客户端,生成的Web Service代理有一个属性叫authHeader。客户端必须把这个属性设置成authHeader的一个新实例,并且把它的authToken属性设置到从LogOn返回的key上:

Private _inv As com.devxpert.Invoicing
Private Sub btnLogOn_Click(ByVal sender As System.Object, _ 
ByVal e As System.EventArgs) _ 
Handles btnLogOn.Click
  _inv = New com.devxpert.Invoicing()
  'logon to the Web service
  Dim authKey As String = _inv.Logon(txtUserName.Text, txtPwd.Text)
  'supply the header
  _inv.authHeaderValue = New com.devxpert.authHeader()
  _inv.authHeaderValue.authToken = authKey
End Sub


				图2.
图2. 认证配置
当客户端第一次实例化Web Service代理的时候,它需要采取这些步骤。它可以保留这个代理实例,你可以不用登录或者再次设置报头就可以使用它。

使用SOAP headers来发送认证key,你可以保持Web Service代码简单和清晰,你允许客户端设置一次报头的值就可以重复地使用它。这个架构是清晰的,并能够使客户端和服务端代码更易读。


 

面对拒绝服务的攻击
你在Web Service里实现安全之前,你必须注意两点。第一,如果你的安全子系统使用先前的资源,比如用数据库连接来决定请求用户是否允许访问,你就直接面临着“拒绝服务“的攻击。你需要调整能解决这个问题的子系统。

发送到客户端之前,你可以通过建立一个无用信息的认证key和把hash和key连接起来调整它。然后,对客户端的每个请求,得到key和hash,并追加得到的key和hash到系统中。你可以为你从客户端得到的key很轻松的重新建立hash,然后与你从客户端得到的hash相比较。如果他们不匹配,说明key是无效的,你甚至不用连接数据库去寻找key。在一个MSDN的文章中,Mary Kirtland描述了这个机制和MSDN组如何在“Favorites” Web Service中使用它(见资源)。

在使用其他人的Web Services时,你应该注意另外一点。如果你在你的应用程序中使用公共的Web Services,你有责任确保你的应用程序能够处理当Web Service遇到拒绝服务式攻击或者其它原因而崩溃掉时的情况。编写你的客户端代码时考虑最坏的情况。如果客户端是一个紧急使命的应用程序,你不应该使它的核心功能依赖于公共的Web Service。

你在执行Web Services安全时有许多选择。你的选择将依赖几个因素,包括网络环境和客户端能力。实现你自己的认证机制可以给你最大的灵活性。使用正确的架构,Web Services安全可以既容易实现又容易使用。

posted on 2005-02-21 09:48  ericzhang  阅读(431)  评论(0编辑  收藏  举报