对于任何一个企业级应用来说,安全(Security)都是一个不可回避的话题。如何识别用户的身份?如何将用户可执行的操作和可访问的资源限制在其允许的权限范围之内?如何记录用户行为,让相应的操作都有据可查?这些都是应用的安全机制或者安全框架需要考虑的典型问题,它们分别对应着三个安全行为:认证(Authentication)、授权(Authorization)和审核(Auditing)。
除了这些典型的安全问题,对于一个以消息作为通信手段的分布式应用,还需要考虑消息的保护(Message Protection)问题。具体来讲,消息保护机制主要包括签名(Signature)和加密(Encryption)。签名确保消息的一致性(Message Integrity),即保证消息在最初发送者和最终接收者之间没有被第三方篡改。而加密确保消息的机密性(Message Confidentiality),即保证消息的内容仅仅对发送者期望的接收者可见。
作为Windows平台下最完备的分布式通信平台,WCF具有一套强大的、完整的的可扩展安全体系,对上述的安全问题提供有效的支持。由于安全体系在整个WCF框架体系中具有极其重大的分量,在接下来的系列文章中,我将深入探讨这一块在我之前的文章中一直不曾触碰的境地。由于安全体系在整个WCF框架体系中具有极其重大的分量,我特意将其分成两个部分:传输安全(主要涉及对认证、消息一致性和消息机密性的实现)和授权与审核。
WCF是一个以消息作为通信手段的分布式编程平台,使我们可以将某些可复用的功能以服务的方式进行定义,并最终部署于分布式网络环境中的某个节点,供潜在的服务消费者调用。服务和调用服务的客户端可以同时存在一个相同的网络,也可以跨越不同的网络,甚至需要借助于Internet。网络的不确定性为分布式应用带来了一系列安全隐患,在正式投入介绍WCF的传输安全之前,我们先来介绍一下这些安全隐患。
一、分布式应用中的传输安全隐患
我们可以将WCF看成是一个消息处理框架,整个框架大体分成两个部分,客户端和服务端。客户端负责请求消息的发送和回复消息的接收,而服务端则负责请求消息的接收和回复消息的发送。WCF只能控制对消息发送前和接收后的处理,而对发送后到接收前这一段消息传输过程却无能为力。而正是消息传输的网络不能提供足够的安全保障,会带入如下一些典型的安全隐患:
- 消息的篡改:传输中的消息被某些网络拦截工具捕获,并被恶意篡改后转发给消息接收者。消息接收者接收会做出错误的操作或者返回给客户端错误的结果。比如说客户端调用服务进行银行转账,该请求会包含转出和转入银行帐号和转入金额,如果网络黑客截获该消息后改变了相应的账号或者金额数据,并将篡改后的请求进行转发。如何服务端将接收到的消息当成是正常的转账请求,后果可想而知;
- 敏感信息的泄漏:消息往往中包含一些敏感的信息,比如信用卡号、身份证号等,如何这些消息以明文的方式在网络中被传输,被网络拦截工具捕获后很自然地会被恶意的黑客看到;
- 钓鱼攻击(Phishing Attack):针对服务A的请求被恶意重新定位到另一个服务B,以执行一些损害访问者利益的操作,或者窃取访问者相关的一些敏感信息。同样以网银服务为例,用户调用网银认证服务并提供自己的帐号和密码进行登录。如果访问者在进行服务调用之前没有验证自己试图访问服务的真实身份,贸然地将自己的网银帐号的密码作为请求发送出去,如果该请求被定位到一个黑客或者流氓软件厂商伪造的服务,它将有可以直接获取到你提供的帐号和密码信息;
- 重放攻击(Replay Attack):黑客利用网络拦截工具捕获针对某个服务的访问请求,然后对该请求进行复制并以一个非常高的频率对目标服务发起调用。如果服务方将每一个接收到的消息都当成是来自正常访问者的请求,来者不拒,最终将会耗尽服务端的可用资源并崩溃。
上面给出了分布式网络应用中由于网络环境的不确定性导致的几个比较典型的安全隐患,实际上,由于网络协议本身并不提供足够的安全保障,我们还会遇到其它很多网络完全问题。网络安全问题在Internet环境下尤为突出,Internet从其产生以来在一段不算长的时间内得到如此迅猛发展,以致改变了我们生活、学习以致思维方式,其中一个主要的技术因素在于它建立在一个“简单”的HTTP协议之上。但是,也正是HTTP的简单性,导致其自身并不能提供足够的安全保障机制。总之,为了弥补网络协议本身对安全保障的局限,我们往往不得不在应用级别重建安全体系。
但是,安全是一个相对“高级”的话题,构建一个适合具体应用要求的安全体系对应用的开发和架构人员具有较高的要求。对于一般的中小规模的分布式应用,投入到安全架构方面的成本完全有可能超过实现业务应用模块的总和。而且,花大力气构建的安全体系可能并不会你想象的那么安全。所以,将安全的实现完全下方到具体的应用也是不太现实的。
既然在网络通信层面不能提供足够的安全保障,而在应用层面去实现安全保障也不太现实,那么我们只能将整个安全保障体系构建于两者之间,我们可以的解决方案姑且称为平台级别或者框架级别的安全保障体系。
作为分布式开发平台的WCF为我们实现了一个功能齐全的、可扩展的安全架构体系,能够满足绝大部分分布式应用场景的安全需求。作为建立在WCF上的分布式应用的架构人员,只需要根据自身的场景进行相应的配置即可。换言之,WCF为每一个具体的安全问题提供了一系列现成的实现方案,安全架构人员只需要根据应用所面临的具体需求对每一个具体安全问题选择一个最适合的方案,然后将它们组合在一起就是最终的安全保障的实现方案。接下来,我们来简单地介绍一下WCF传输安全体系如何解决上述的这些网络安全隐患。
在本篇文章的一开始我们就提到了,WCF的传输安全旨在解决三个典型的安全问题,即认证、消息一致性和消息机密性,我们下来讨论一下什么是认证,如何实现认证。
二、认证(Authentication)
在冷兵器时代,为了抵御敌方入侵取保城池安全,需要修筑坚固的城墙,挖掘较深的护城河。实际上,城池二字原本的含义就是指代的城墙和护城河。对于一些兵家必争的军事重镇,往往具有不止一道坚城,比较典型防御体系就是修筑内城和外城。这样防线设置确保你在外层防线被突破之后依然退回到第二道防线进行继续抵抗。
对于当代战争,虽然高城深池起不了任何的防御作用,但是这样设置多道安全屏障的防御体系的理念依然是目前构建网络安全体系的指导方针。以一个分布式的Web应用为例,我们可以在Web服务器和应用服务器设置防火墙。前者确保Web服务器的安全,使第一道防线。即时黑客突破该防线,依然可以保障核心业务的安全,因为这样的逻辑处理都是一服务的形式部署在应用服务器上。
对于具体的应用来说,在很大程度上讲,安全的含义就是让某个人做他可以做的事情。应用为用户提供对某项功能的实现,或者实现对某种资源的存取,但是操作的执行和资源的访问必须依赖于用户自身的权限。也就是说,我们需要确保在用户既定权限范围内提供为该用户提供服务,这就是我们所说的授权(Authorization)或者存取控制(Access Control)。
但是,对用户进行合理授权的一个前提是当前用户身份已经被确认,这种对访问者身份识别和鉴定的行为被称为认证(Authentication)。认证往往是应用或者服务安全体系的第一道屏障,如果没有了这道屏障,后续的安全防线将形同虚设。认证帮助我们确认“谁在敲打我的门?”。应用或者服务的访问者以一个它申明的身份叩响第一道城门,看门人只有在成功确定对方身份无误之后方能为其开启方便之门,否则直接将其扫地出门。
如果要给认证下一个定义,我个人的倾向这样的定义:认证是确定被认证方的真实身份和他或她申明(Claim)的身份是否相符的行为。认证方需要被认证方提供相应的身份证明材料,以鉴定本身的身份是否与声称的身份相符。在计算机的语言中,这里的身份证明有一个专有的名称,即“凭证(Credential)”,或者用户凭证(User Credential)、认证凭证(Authentication Credential)。
三、用户凭证(User Credential)
最好的设计就是能够尽可能的模拟现实。对于安全认证来说,在现实生活中有无数现成的例子。比如我对一个不认识的人说:“我是张三”,对方如何才能相信我真的是张三而非李四呢?虽然我们未必全都是有身份的人,但无疑我们都是有身份证的人,身份证可以证明我们的真实身份。而这里的身份证就是一种典型的用户凭证。
认证方能够根据本认证方提供的身份证识别对方的真实身份,必须满足三个条件:其一,被认证人声称是身份证上注明的那个人;其二,身份证的持有者就是身份证的拥有者;其三,身份证本身是合法有效的,即使通过公安机关颁发的,而不是通过拨打“办证”电话办理的。第一个问题一般不是问题,因为对于一个神经稍微正常的人来说,他不会拿着李四的身份证去证明自己是张三;第二个问题可以根据身份证上面的照片来判断;第三个问题就依赖于身份证本身的防伪标识和认证方的鉴别能力了。
上述的三个条件本质上也反映了,能够基于用户凭证的认证过程中凭证本身应该具有的属性,以及凭证和本认证人的关系,即:凭证与声明的一致性,被认证人对凭证的拥有性,以及凭证的合法性。为了简单,我们不妨简称为用户凭证的三个属性。用户凭证的类型决定了认证的方式,WCF支持一系列不同类型的用户凭证,以满足不同认证需求。接下来,我们按照上述的这三点来简单介绍几种使用比较普遍的用户凭证以及相应的认证方式。
用户名/密码
我们最常使用的认证方式莫过于采用验证用户名和密码的形式,以致于我们提到身份验证,很多人会想到密码。最为常用的凭证类型,用户名/密码凭证由两个要素构成,即用户名和密码。我们不妨通过上面我们讲到的用户凭证的三属性来分析用户名/密码凭证。
前者表示被认证方声明的身份(Identity),后者是持有人是凭证合法拥有者的证据。对于认证方来说,由于账号对应的密码属于账号拥有者的私密信息,如果被认证方能够提供与他声明身份相匹配的密码,就能够证明对方确实与他声明的是同一个人。首先,用户名代表身份(Identify),凭证与声明的一致性意味着本认证方声明的身份与用户名一致;被认证人对凭证的拥有性通过密码证明,密码属于绝对隐私信息,被认证人如果能够提供与所声明的身份相匹配的密码,就能够证明他是凭证的真正拥有者;由于用户名/密码凭证不属于证书型凭证,不需要合法机构颁发,对于合法性则无从说起。
在采用用户名/密码认证方式的应用中,认证方一般具有所有用户帐号和密码的列表。当然,由于密码对属于持有人的决定隐私,原则上仅限于持有人本人知晓,其他人任何人(当然也包括认证方)不应该采用技术手段获知该密码。如果认证方维护者他负责认证的所有帐户的用户名和密码的列表,本存储的一般是原始密码的哈希值以及进行哈希运算采用的Key。由于哈希算法是不可逆的,所以无法通过后哈希的值和相应的Key得到原始的值,从而确保了密码的安全性。在进行认证的时候,只需要根据用户名找到相应的Key,然后利用该Key采用相同的算法对用户提供的密码进行哈希算法,最终将最终的运算结果和本地存储的值进行比较即可验证密码的真伪。
虽然在我们进行项目开发的时候,我们也会选在对用户注册时提供的密码进行加密存储,这样可以让用户忘记原来的密码的时候,通过向认证方证明其真实身份的前提下,让认证方通过通过解密返回其原来的密码。对于密码的加密存储问题,无论是采用对称加密还是非对称加密,我们都可以通过相应的解密算法得到其原始密码,所以从理论上讲具有安全问题。不过,具体应用在选择密码存储策略的时候,可以根据自身所需的安全级别以及是否需要返回原始密码,选择对原始密码进行哈希或者加密。但是,无论如何对密码进行明文存储是不被允许的。
那么WCF服务端对于客户端提供的用户名/密码用户凭证,应该采用怎样的验证手段呢?在基于用户名/密码的验证规则方面,WCF的安全框架体系为你提供了多种方案。个人觉得,最具有价值的采用基于ASP.NET Membership的认证。关于ASP.NET Membership认证,相信使用过ASP.NET 2.0以及之后版本的读者应该不感到陌生。
我们还可以将客户端提供的用户名和密码映射为Windows账号和相应的密码,这样我们就可以采用Windows认证识别客户端的真实身份。如果这两种方式依然不能满足你对于基于用户名/密码的认证需求,你可以自行定义认证实现,你只需要创建一个继承于System.IdentityModel.Selectors.UserNamePasswordValidator类型的类就可以。
Windows凭证
应该说就采用的频率程度,集成Windows认证(IWA:Integrated Windows Authentication)是仅次于用户名/密码的认证方式。尤其是在基于Windows活动目录(AD:Active Directory)的Intranet应用来说,Windows认证更是成为首选。微软几乎所有需要进行认证的产品或者开发平台都集成了Windows认证,比如IIS,SQL Server,ASP.NET等,当然,WCF也不可能例外。
Windows是实现单点登录(SSO:Single Sign-On)最理想的方式。无论是采用域(Domain)模式还是工作组(Workgroup)模式,只要你以Windows帐号和密码登录到某一台机器,你就会得到一个凭证。在当前会话超时之前,你就可以携带该Windows凭证,自动登录到集成了Windows认证方式的所有应用,而无须频繁地输入相同的Windows帐号和密码。如果登录帐号不具有操作目标应用的权限,在一般情况下,你好可以通过重新输入Windows帐号和相应的密码(如果当前用户具有多个Windows帐号)以另外一个身份(该身份具有对目标应用进行操作的访问权限)对目标应用进行操作。
对于WCF的Windows与之类似,在不考虑模拟(Impersonation)和委托(Delegation)的情况下,WCF客户端安全框架自动将客户端应用进程的Windows凭证,作为调用服务的客户段凭证发送给服务进行认证。此外,在编写客户端程序的时候,我们还可以通过指定Windows用户名和密码动态地创建Windows凭证,并将其作为客户端凭证进行服务的调用。Windows凭证需要通过提供Window帐号和相匹配的密码来获取,从性质上也可以看成是用户名/密码凭证的变体,我们可以照用户名/密码凭证的方式来分析Windows凭证的三个属性。
就其实现来说,Windows具有两种不同的认证协议,即NTLM(NT LAN Manager)和Kerberos,分别作为工作组和域模式下的网络认证协议。由于在具体的服务调用环境中,采用的Windows凭证实际上分别是NTML或者Kerberos票据。
在下篇中我们将会讨论另一种重要的用户凭证,即数字证书,以及由此引出的关于非对称密码学(或者公钥密码学)。下篇文章不仅仅会为你深入数字证书背后的原理,还会让你对数字签名和加密有一个深刻的了解。
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。