背景
Web service最吸引人的特性就是Loosely coupled, 利用它企业能够及时的将业务中的变革和创新体现在对外提供的服务(Web Service)上(更新现有服务的实现或创建新的服务),还可以更加高效的集成企业与其合作伙伴的应用价值链。然而,实现这一目标的前提是拥有方便有效的安全保障,如果没有适当的安全保障或者需要复杂的安全保障都将影响企业间广泛存在的服务交互。试想如果企业A的员工访问其合作伙伴提供的时候服务时需要不断重复的输入自己的验证信息,那么无论在服务的方便性或安全性上都会给用户以不好的体验,从而影响服务的集成,限制Web Service在企业集成中所能发挥的作用。
假设存在如下一个场景:
John Doe是一家叫做Pharma456.cm医药公司的职员,John在自己的公司拥有一个账户,用于访问本公司的服务。Pharma456.com公司有一些合作伙伴,作为公司的职员,可以在其合作伙伴的公司所提供的服务中享有某些优惠。在本例中,Pharma456.com公司分别有一家财务公司(RetireAccounts.com)和一家投资公司(InvestAccounts.com)作为它的合作伙伴,它们分别可以为Pharma456.com公司的职员提供储蓄服务和投资服务,并给予一定的优惠。此时,如果John想以合作伙伴公司职员的身份使用RetireAccounts.com的储蓄服务,就必须向RetireAccounts.com公司提供自己的SSN(Social Security Number), Pharma456.com公司给自己的特定标识等等信息。如果没有一种身份联邦的机制,那么即使John已经在自己的公司验证过身份而且是在自己公司的网站上调用的RetireAccounts.com的储蓄服务,但是为了使用RetireAccounts.com的储蓄服务,他不得不再次到RetireAccounts.com公司验证自己的身份。而RetireAccounts.com公司并没有John的身份信息,无法实现对他的身份鉴别。
现有的解决方案
上述问题由来已久,以往主要有两种方法解决这个问题。第一种方法就是RetireAccounts.com公司将收到的John的身份信息返回给Pharma456.com公司,让Pharma456.com公司验证John的身份,并将验证结果返回。还有一种方法就是让RetireAccounts.com公司复制Pharma456.com公司的所有身份信息,这样RetireAccounts.com公司也就可以独立完成对Pharma456.com公司的职员的身份鉴别了。
存在的问题
可以想象这两种方法都不是好的解决方案。前者需要各个公司之间的身份鉴别服务能互相访问,而将身份鉴别服务暴露在外将带来很多安全方面的问题(例如泄漏机密), 即便不考虑安全方面的问题, 为了实现身份鉴别,服务之间的消息传递就又多了一次往返,既容易出问题(网络是很不可靠的)而且效率低下。后者虽然避免了将身份鉴别服务暴露在外, 但是实际上很难及时的实现用户信息的同步, 如果John离开了Pharma456.com公司, 如何保证RetireAccounts.com公司中的用户信息得到及时的更新是件困难的事情. 另一方面, 由于用户信息涉及一些敏感数据,出于个人隐私和公司利益的考虑, 把一个公司的所有用户信息全部复制给另一个公司也是不太现实的.
新的方案
基于以上两种方案存在的问题, 得到以下结论:
1. 让用户在本地完成身份鉴别, 不要将用户鉴别服务对外公开。
2. 不希望将一个企业的用户信息复制到其他企业.
3. 不希望用户重复输入自己的安全凭证.
要想达到这三个要求, 那么必须使得用户信息可以在企业间移动, 实现Portal Identity.
具体过程如下:
用户首先在他所在的安全领域(主域)进行身份鉴别, 鉴别通过后他可以获得一个经过鉴别服务签名的有关他自己身份信息的断言, 其中可能包含了服务所需的某些属性,比如用户的年龄等等。当用户向其他合作伙伴公司的服务发出请求的时候,将该从主域中获得的断言同时发送过去. 这样服务就可以根据断言独立的判断出用户是否是他所在主域的合法用户, 并从断言中获得该用户的相关属性甚至权限信息.
如何描述该断言? 如果每个公司采用不同的描述方法, 那么该方法就失去了实用价值, 业界为此制定了一套描述用户安全信息的规范---SAML(Security Assertion Mark Language). 利用它我们可以在验证了某个用户后, 将验证信息和其他一些与用户相关的信息以SAML的格式发送给需要验证该用户身份的其他企业. 这样用户就无需再向各个服务重复的提供自己在主域中的安全凭证, 从而实现SSO的功能.
在了解了SAML的应用背景和它的基本原理后, 下面对SAML的语法做简单介绍。
以下是一段用SAML描述用户验证信息(Authentication Statement)的示例:
<?xml version="1.0" encoding="utf-8"?>
<Assertion
xmlns="urn:oasis:names:tc:SAML:1.0:assertion"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
MajorVersion="1"
MinorVersion="0"
AssertionID="http://www. Pharma456.com/AuthenticationService/SAMLAssertions/786"
Issuer="http://www. Pharma456.com"
IssueInstant="2006-04-29T02:00:00.173Z">
<Conditions
NotBefore="2006-04-29T02:00:00.173Z"
NotOnOrAfter="2006-04-30T02:00:00.173Z"/>
<AttributeStatment
AuthenticationMethod="urn:oasis:names:tc:SAML:1.0:am:password"
AuthenticationInstant="2006-04-29T02:00:00.173Z">
<Subject>
<NameIdentifier
NameQualifier="http://www.Pharma456.com">
John Doe
</NameIdentifier>
<SubjectConfirmation>
<ConfirmationMethod>
urn:oasis:names:tc:SAML:1.0:cm:holder-of-key
</ConfirmationMethod>
<ds:KeyInfo>
<ds:KeyValue> ... </ds:KeyValue>
</ds:KeyInfo>
</SubjectConfirmation>
</Subject>
</AttributeStatment>
<ds:Signature>...</ds:Signature>
</Assertion>
上述内容概括起来表述了以下事实:
一个名为 MyTourOperator的主体被验证为某个的密钥的拥有者, 验证过程采用了基于密码的方法,验证发生的时间是2006-04-29T02:00:00.173Z. 该断言的有效期为2006-04-29T02:00:00.173Z 至2006-04-30T02:00:00.173Z
SAML中的Assertion主要包含了三个子元素. Conditions, AuthenticationStatement和 Signature.
Conditons元素在这里通过NotBefore和NotOnOrAfter属性描述了该断言的有效期. Singnature元素是断言的发布者对断言的签名,以确保该断言没有被修改,并体现出该断言发布者的身份.这两个元素主要起了辅助性作用, AuthenticationStatement元素才表达出了验证过程的结果.
在AuthenticationStatement元素中包含了两个属性和一个子元素. 第一个属性是AuthenticationMethod,它描述了主体(Subject)在验证所使用的方法,这里采用了Password方法,还有其他方法如Kerberos,X.509 PKI等等. 第二个属性AuthenticationInstant描述了验证所发生的时间。唯一的元素Subject描述了被验证的主体,其中又包含了NameIdentifier和SubjectConfirmation两个子元素。NameIdentifier是主体的标识,这里主体的名称是MyTourOperator,NameQualifier属性的作用和名空间类似,是为了避免主体的名称发生重复。SubjectConfirmation元素用于描述被验证的主体和消息(这里的消息指的是含有SAML Assertion的消息)的发送者的关系。这个元素在Assertion中很重要,只有通过它我们才能判断出消息(Assertion)的发送者和Subject的关系,从而安全的使用Assertion中所包含的安全信息。
在本例中通过ConfirmationMethod属性设定了使用holder-of-key作为主体确认方法,并在ds:KeyInfo中给出了有关Key的信息。通过拥有某个密钥来证明自己的身份在安全领域是一种常用的方法,例如在本例通过ds:KeyInfo给出主体的公钥信息,此时如果消息(Assertion)的发送方能使用主体的私钥签名一段消息. 那么我们就可以证明消息的发送方确实和断言中的主体身份一致. 否则就算某用户盗窃了某个主体的断言将它随消息发送给服务方, 但是由于它没有该主体的私钥,不能使用私钥对消息做签名, 那么服务方在通过ds:KeyInfo中的公钥验证消息签名的时候, 仍然能判断出该用户在冒充主体. 同样通过对称密钥也可以实现以上功能, 只不过为了不在消息中泄漏该对称密钥会用到EncrytedKey来描述KeyInfo.
除了在这里出现的holder-of-key主体确认方法, 还有一种Sender-vouches的方法. 既然服务信任用户主域所在的SAML Authority, 那么就借助这个信任关系, 用户把对服务的请求都转由它所在主域的SAML Authority来发送,而不是采用在自己发送消息上附上SAML Assertion的方法.如下图所示:
至此我们已经对最初的那份SAML Assertion做了详细的介绍, 回忆一下它描述的内容:
一个名为 MyTourOperator的主体被验证为某个的密钥的拥有者, 验证过程采用了基于密码的方法,验证发生的时间是2006-04-29T02:00:00.173Z. 该断言的有效期为2006-04-29T02:00:00.173Z 至2006-04-30T02:00:00.173Z
根据这份描述, 服务方可以判定主体是被它所在主域验证了的合法用户, 但是从中并不能获得其他额外的信息, 比如Saving Service可能需要的身份证号码等等一些有关Service的属性.
为了让SAML能够成为真正的Portable Identity. SAML 断言还支持对主体属性的申明.
以下便是一份对主体属性的SAML Assertion
<?xml version="1.0" encoding="utf-8"?>
<Assertion
xmlns="urn:oasis:names:tc:SAML:1.0:assertion"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
MajorVersion="1"
MinorVersion="0"
AssertionID="http://www. Pharma456.com/AuthenticationService/SAMLAssertions/786"
Issuer="http://www. Pharma456.com"
IssueInstant="2006-04-29T02:00:00.173Z">
<Conditions
NotBefore="2006-04-29T02:00:00.173Z"
NotOnOrAfter="2006-04-30T02:00:00.173Z"/>
<AttributeStatment
AuthenticationMethod="urn:oasis:names:tc:SAML:1.0:am:password"
AuthenticationInstant="2006-04-29T02:00:00.173Z">
<Subject>
<NameIdentifier
NameQualifier="http://www.Pharma456.com">
John Doe
</NameIdentifier>
<SubjectConfirmation>
<ConfirmationMethod>
urn:oasis:names:tc:SAML:1.0:cm:holder-of-key
</ConfirmationMethod>
<ds:KeyInfo>
<ds:KeyValue> ... </ds:KeyValue>
</ds:KeyInfo>
</SubjectConfirmation>
</Subject>
<Attribute AttributeName="SSN"
AttributeNameSpace="http://www.Pharma456.com/AttributeService">
<AttributeValue>3201012820912381 </AttributeValue>
</Attribute>
</AttributeStatment>
<ds:Signature>...</ds:Signature>
</Assertion>
在断言中,AuthenticationStatement被AttributeStatment元素所替代。仔细观察其中并没有太大的差别,仅仅是在AttributeStatment元素中多出了一个Attribute的子元素。Attribute元素包含了AttributeName和AttributeNamespace属性以及AttributeValue的子元素。其中AttributeName表示属性名(如这里的SSN表示身份证号码),AttributeNamespace用于避免命名冲突. AttributeValue中放置属性的值. 值得注意的是在SAML规范中并没有预置某些属性, 所有的属性均由用户根据需要自定义(其实这里也会涉及到一个互操作性的问题).
基于以上信息, 新的包含主体属性的SAML Assertion描述了以下事实:
一个名为 MyTourOperator的主体被验证为某个的密钥的拥有者,他的身份证号码是3201012820912381 验证过程采用了基于密码的方法,验证发生的时间是
在介绍了两种类型的SAML Assertion以及它们的语法和主体确认方法后, 我们需要了解如何将它们与SOAP绑定, 从而利用它们来保障Web Service的安全的.
WS-Security中通过Security Token的方式将已有的安全技术很好的集成到Web Service的环境中, 利用它们为XML Security(XML Signature, XML Encryption)服务, 进而实现Web Service的安全. 其中包含了使用传统Username&Password方式的UsernameToken, 还有使用了Kerberos协议以及X.509证书的BinarySecurityToken. 现在为了将SAML引入Web Service的环境, 我们为此定义了使用SAML Assertion的XMLToken,以下是使用SAML Token的SOAP 消息示例:
<?xml version="1.0" encoding="utf-8"?>
<SOAP:Envelope
xmlns:SOAP="http://www.w3.org/2001/12/soap-envelope"
xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/xx/secext"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<SOAP:Header>
<wsse:Security>
<saml:Assertion
xmlns="urn:oasis:names:tc:SAML:1.0:assertion"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
MajorVersion="1"
MinorVersion="0"
AssertionID="http://www. Pharma456.com/AuthenticationService/SAMLAssertions/786"
Issuer="ttp://www. Pharma456.com"
IssueInstant="006-04-29T02:00:00.173Z">
<Conditions
NotBefore="006-04-29T02:00:00.173Z"
NotOnOrAfter="006-04-30T02:00:00.173Z">
<AttributeStatment
AuthenticationMethod="urn:oasis:names:tc:SAML:1.0:am:password"
AuthenticationInstant="2006-04-29T02:00:00.173Z">
<Subject>
<NameIdentifier
NameQualifier="http://www.Pharma456.com">
John Doe
</NameIdentifier>
<SubjectConfirmation>
<ConfirmationMethod>
urn:oasis:names:tc:SAML:1.0:cm:holder-of-key
</ConfirmationMethod>
<ds:KeyInfo>
<ds:KeyValue> ... </ds:KeyValue>
</ds:KeyInfo>
</SubjectConfirmation>
</Subject>
<Attribute AttributeName="SSN"
AttributeNameSpace="http://www.Pharma456.com/AttributeService">
<AttributeValue>3201012820912381 </AttributeValue>
</Attribute>
</AttributeStatment>
<ds:Signature>...</ds:Signature>
</saml:Assertion>
<ds:Signature>
<ds:SignedInfo>
<ds:CanonicalizationMethod
Algorithm="http://www.w3.org/2001/10/xml -exc-c14n# "/>
<ds:SignatureMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="# createAccountRequest ">
<ds:Transforms>
<ds:Transform
Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>BSDFHJYK21f...</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
GKLKAJFLASKJ52kjKJKLJ345KKKJ...
</ds:SignatureValue>
<ds:KeyInfo>
<wsse:SecurityTokenReference wsu:Id="STR1"
wsse11:TokenType=".../oasis-wss-saml-token-profile-1.1#SAMLV1.1">
<wsse:KeyIdentifier wsu:Id="…"
ValueType=".../oasis-wss-saml-token-profile-1.0#SAMLAssertionID">
http://www.Pharma456.com/AuthenticationService/SAMLAssertions/786
</wsse:KeyIdentifier>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
</wsse:Security>
</SOAP:Header>
<SOAP-ENV:Body>
<s:CreateAccount
xmlns:s=“http://www.RetireAccounts.com/partnerservice/”
ID="createAccountRequest">
<!--Parameters passed with the method call-->
</s: CreateAccount >
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
现在我们已经将SAML Assertion运用到了WS-Security中, 并完全理解了它的原理. 但是有一个重要的问题却被我们所遗漏, SAML Assertion从何而来?
Web Services Security 系列文章
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架