纯约束型协议 : OAuth , SAML , OIDC , CAS
服务器类协议 : RADIUS , Kerberos , ADFS
认证方式类 : OTP , 生物认证 (人脸 , 声纹 , 指纹)
认证服务器(附带) : AD , LDAP
这一篇主要说SAML , 这货老而弥坚 !
一 . 前言
SAML 其实算是一种格式规范 , 他的全称是安全断言标记语言(英语:Security Assertion Markup Language,简称SAML,发音sam-el)是一个基于XML的开源标准数据格式 . 而我们应用中的 SAML 是一种宏观实现 ,通过 SAML 格式来传输认证信息 .
通常情况下 , 开发人员解除到的都是 OAuth , 第一印象往往会感觉 SAML 是一个很
'古老'
的协议 , 但是接触多了就会发现 ,SAML 在一些重量级的应用里面随处可见 , 比如 老牌的Windows Server , 其中就大量使用了 SAML 认证 , 这协议确实老而弥坚 .
一句话说清楚 SAML 是什么流程 :
IDP Metadata 为 SSO (Server ) 元数据 , SP Metadata 为 Client 元数据 , 元数据文件中包含其本身的认证数据 (认证地址 , 签名 , 公钥等) , Server 和 Client 互相持有对方的元数据 , 通过对方元数据中的公钥进行加密 ,通过签名校验合法性 , 再通过 认证地址进行请求或者回调 .
二 . SAML 主要概念
2.1 行为概念
用一句话来解释其中的关联 : 服务端(客户端) 读取元数据信息, 使用指定的协议 , 通过绑定 , 将断言发送给对方 !
断言 (Assertions) 即信息
:
断言是在 SAML 中用来描述认证的对象,其中包括一个用户在什么时间、以什么方式被认证,同时还可以包括一些扩展信息,比如用户的 Email 地址和电话等等。
协议 (Protocol) 即通信
:
协议规定如何执行不同的行为。这些行为被细化成一些列的 Request 和 Response 对象,而在这些请求和相应的对象中包含了行为所特别需要的信息。比如,认证请求协议(AuthnRequest Protocol)就规定了一个 SP 如何请求去获得一个被认证的与用户。
绑定 (Binding) 即传输
:
绑定定义了 SAML 信息如何使用通信协议被传输的。比如,HTTP 重定向绑定,即声明 SAML 信息将通过 HTTP 重定向消息传输;再比如 SAML SOAP 绑定,声明了通过 SOAP 来传递 SAML 消息。比如上面 AuthnRequest 就声明了 Http-POst 绑定
元数据 (MetaData)
:
SAML 的元数据是配置数据,其包含关于 SAML 通信各方的信息,比如通信另一方的 ID、Web Service 的 IP 地址、所支持的绑定类型以及通信中实用的密钥等等。
2.2 说明概念
认证声明:声明用户是否已经认证,通常用于单点登录。
属性声明:声明某个 Subject 所具有的属性。
授权决策声明:声明某个资源的权限,即一个用户在资源 R 上具有给定的 E 权限而能够执行 A 操作。
2.3 元数据层次
MeteData 文件中总共有四个层次 :
层次 一 : Assertion : 断言 <saml:Assertion>
层次 二 : Protocols :规定如何请求(samlrequest)和回复(samlresponse)saml消息,当然包含assertion消息<samlp:AuthnRequest> + <samlp:Response>
层次 三 :Bindings : 绑定 ,决定用何种方式进行传输
层次 四 :Profile : 配套方案 ,
2.4 证书作用
之前说了2点 ,
一个是Server/Client 持有双方的元数据 , 一个元数据中包含了详细的证书,密钥信息
,具体的元数据文件我们后面再说 , 这里先说说元数据里面的证书 .
可信实体包含公钥的证书会以X.509证书格式发布在metadata中,而对应的私钥则安全保存在本地。
这些密钥被用于消息层面的签名和加密,而SAML消息在传输过程中由TLS协议来进行安全交换。
阶段一 : 当IDP 拿到 SP 的请求时 , 证书的作用并不明显 , 主要有如下的作用
确定请求来着信任的 SP
阶段二 : 当 SP 获取 IDP 的反馈时 , SP 会做以下几件事
通过签名确定来自已知的 IDP
获取使用IDP私钥签名的内容
使用 IDP 公钥验证签名
- signing : 签名
- encryption : 加密
- sign : 签名
- encryption : 加密
- ds:Signature : IDP 密钥信息
IDP Metedata 文件参考
<EntityDescriptor
xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
xmlns:shibmd="urn:mace:shibboleth:metadata:1.0"
xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui" entityID="http://127.0.0.1/samlServer/idp">
<IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol urn:mace:shibboleth:1.0">
<Extensions>
<shibmd:Scope regexp="false">scope</shibmd:Scope>
</Extensions>
<KeyDescriptor use="signing">
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>...[签名信息]...</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</KeyDescriptor>
<KeyDescriptor use="encryption">
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>...[公钥信息]...</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</KeyDescriptor>
<NameIDFormat>urn:mace:shibboleth:1.0:nameIdentifier</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://127.0.0.1/samlServer/idp/profile/SAML2/POST/SLO"/>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://127.0.0.1/samlServer/idp/profile/SAML2/POST/SSO"/>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://127.0.0.1/samlServer/idp/profile/SAML2/Redirect/SSO"/>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://127.0.0.1/sso/idp/profile/SAML2/SOAP/ECP"/>
</IDPSSODescriptor>
</EntityDescriptor>
SP Metedata 文件参考
<?xml version="1.0" encoding="UTF-8"?><md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" ID="_5p2k1rpmlbr0hphrontb6zwiplqac1xxpzvzdma" entityID="http://127.0.0.1:9081/mfa-client/saml/callback" validUntil="2040-05-07T14:08:50.499Z">
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference URI="#_5p2k1rpmlbr0hphrontb6zwiplqac1xxpzvzdma">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>GSp3lIKMQs70Q6FQYHWFhVaGKJv31AiRTuXOcyO78mk=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
...[IDP签名信息]...
</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>
...[IDP公钥信息]...
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
<md:Extensions xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport">
<alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"/>
<alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"/>
<alg:SigningMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256"/>
<alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384"/>
<alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512"/>
<alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1"/>
<alg:SigningMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1"/>
<alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#hmac-sha256"/>
<alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#hmac-sha384"/>
<alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#hmac-sha512"/>
<alg:SigningMethod Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1"/>
<alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#sha384"/>
<alg:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
</md:Extensions>
<md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.0:protocol urn:oasis:names:tc:SAML:1.1:protocol">
<md:Extensions xmlns:init="urn:oasis:names:tc:SAML:profiles:SSO:request-init">
<init:RequestInitiator Binding="urn:oasis:names:tc:SAML:profiles:SSO:request-init" Location="http://127.0.0.1/client/saml/callback?client_name=samlClient"/>
</md:Extensions>
<md:KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>...[签名信息]...</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:KeyDescriptor use="encryption">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>...[公钥信息]...</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://127.0.0.1/client/saml/callback?client_name=samlClient&logoutendpoint=true"/>
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign" Location="http://127.0.0.1/client/saml/callback?client_name=samlClient&logoutendpoint=true"/>
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://127.0.0.1/client/saml/callback?client_name=samlClient&logoutendpoint=true"/>
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://127.0.0.1/client/saml/callback?client_name=samlClient&logoutendpoint=true"/>
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat>
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://127.0.0.1/client/saml/callback?client_name=SAML2Client" index="0"/>
<md:AttributeConsumingService index="0">
<md:RequestedAttribute FriendlyName="eduPersonPrincipalName" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="false"/>
</md:AttributeConsumingService>
</md:SPSSODescriptor>
</md:EntityDescriptor>
三 . SAML 流程
3.1 SAML 成员及交互
SAML 中有2个重要的成员 :
SP(Service Provider) : 向用户提供正式商业服务的实体,通常需要认证一个用户的身份;
IDP(Identity Provider) : 提供用户的身份鉴别,确保用户是其所声明的身份
SAML 请求流程 :
请求端作为一个资源访问者 , 期望访问服务商的资源
SP 发现这是一个未认证的请求 , 此时会返回一个HTML , Form 的隐藏域中有一个 SAML 的认证请求数据包 , 实际上这里不需要手动 , HTML 会通过JS 自动执行到 IDP 中
通过 JS 会自动提交到 IDP 中 , 此时 IDP 接收到请求后 , 返回验证界面( 2-3 步在浏览器端不可见 )
IDP 给浏览器返回一个待登录的页面 (即常规的登录页)
用户进行了认证 , 并且成功提交到 IDP
IDP 组装一个HTML Form , 其中包含一个Response ,Response 中有一个对成功用户的断言 (用户信息及权限) , 有认证情况 , 及私钥签名等
HTML 被自动提交到 SP , 前面说了 第六步的时候会同时返回一个私钥 , 这一步中 ,SP 会通过公钥校验断言是否合法
剩下的就是判断是一个合法用户 ,然后重定向到请求的页面中
SAML 中的成员定位
关于 IDP 和 SP 的角色定位 , 与OAuth不同 , 认证中心和资源服务被刻意的区分为了2个概念 ,在这2个概率里面 , 我一直有一个纠结 : 身份信息到底到谁手上?
关于这个其实不需要太纠结 , IDP 里面是存在身份数据的 , 同样 SP 里面也可能存在身份数据 , 通常而言 , IDP是一个认证中心 , 它认证完成后生成的credential在绝大多数下只会包含一个Username , 然后SP会拿着username 去直接使用或者获取更详细的信息
就像很多业务流程一样 , 认证后返回得仅仅是一个ID , 而具体得Userinfo 放在哪 , 按照业务去规划就行
3.2 SAML 的详细交互
前置条件 : 持有双方的 Metedata
Server 持有 Client 的 Metedata , 其中有 Client 的公钥及重定向地址
Client 持有 Server 的 Metedata , 其中有 Server 的公钥及重定向地址 , 及签名等信息
3.3 SAML 请求格式
参考 @ www.samltool.com/generic_sso…
之前提到了 , Metedata 中包含了 Server / Client 的访问信息 , 例如 :
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://127.0.0.1/samlServer/idp/profile/SAML2/POST/SLO"/>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://127.0.0.1/samlServer/idp/profile/SAML2/POST/SSO"/>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://127.0.0.1/samlServer/idp/profile/SAML2/Redirect/SSO"/>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://127.0.0.1/sso/idp/profile/SAML2/SOAP/ECP"/>
其中清楚的声明了访问的地址以及访问的方式 : 例如 urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST 断言说明通过 Post 形式的请求的访问地址 ,urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect 则说明了通过 Redirect (Get) 请求时的访问地址
SAML AuthnRequest 请求格式
<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="ONELOGIN_809707f0030a5d00620c9d9df97f627afe9dcc24" Version="2.0" ProviderName="SP test" IssueInstant="2014-07-16T23:52:45Z" Destination="http://idp.example.com/SSOService.php" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="http://sp.example.com/demo1/index.php?acs">
<saml:Issuer>http://sp.example.com/demo1/metadata.php</saml:Issuer>
<samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" AllowCreate="true"/>
<samlp:RequestedAuthnContext Comparison="exact">
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
</samlp:RequestedAuthnContext>
</samlp:AuthnRequest>
ID : 新生成的标识号
IssueInstant : 时间戳,表示生成时间
AssertionConsumerServiceURL : 服务提供者的SAML URL接口,标识提供者在此发送身份验证令牌。
Issuer : 服务提供者的EntityID(唯一标识符)
InResponseTo : 此响应所属的SAML请求的ID
Recipient : 服务提供者的EntityID(唯一标识符)
当然实际请求是加密的 :
http:
SAMLRequest=bM441nuRIzAjKeMM8RhegMFjZ4L4xPBHhAfHYqgnYDQnSxC++Qn5IocWuzuBGz7JQmT9C57nxjxgbFIatiqUCQN17aYrLn/mWE09C5mJMYlcV68ibEkbR/JKUQ+2u/N+mSD4/C/QvFvuB6BcJaXaz0h7NwGhHROUte6MoGJKMPE=
&RelayState=http%3A%2F%2F127.0.0.1%2FclientSaml%2Fcallback
3.4 SAML 返回格式案例
参考 @ www.samltool.com/generic_sso…
3.5 Logout 案例
注意 ,实际请求过程中都是加密 ,去这里可以自己解密看看 --> www.samltool.com/attributes.…
Logout Request : www.samltool.com/generic_slo…
<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="ONELOGIN_21df91a89767879fc0f7df6a1490c6000c81644d" Version="2.0" IssueInstant="2014-07-18T01:13:06Z" Destination="http://idp.example.com/SingleLogoutService.php">
<saml:Issuer>http://sp.example.com/demo1/metadata.php</saml:Issuer>
<saml:NameID SPNameQualifier="http://sp.example.com/demo1/metadata.php" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">ONELOGIN_f92cc1834efc0f73e9c09f482fce80037a6251e7</saml:NameID>
</samlp:LogoutRequest>
Logout Response :
<samlp:LogoutResponse xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_6c3737282f007720e736f0f4028feed8cb9b40291c" Version="2.0" IssueInstant="2014-07-18T01:13:06Z" Destination="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_21df91a89767879fc0f7df6a1490c6000c81644d">
<saml:Issuer>http:
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
</samlp:LogoutResponse>
四 . 请求案例
http:
http:
五 . SAML FAQ
5.1 SAML 与 OAuth 的区别
SAML使用XML传递消息,而OAuth使用JavaScript对象表示法
OAuth提供了一种更简单的移动体验 (OAuth广泛使用API调用),而SAML则面向企业安全
OAuth比SAML更适合访问范围。访问范围是一种实践,一旦身份验证,只允许资源/应用中最低限度的访问。
就像之前说的 ,SAML 往往被一些大厂实践 , 是偏企业级的单点方式 , 一般人接触 , 第一感觉就是复杂 , 但是重量级及意味着更多的功能 ,更企业级的安全性 , 各有各的特色
附录 . 补充概念
Assertion :
SAML 消息(XML 文档)的一部分,它提供关于断言主题的事实(通常是关于经过身份验证的用户)。断言可以包含有关身份验证、关联属性或授权决策的信息
Artifact :
标识符,该标识符可用于从标识或使用后台通道绑定的服务提供者检索完整的 SAML 消息
Bnding :
用于传递 SAML 消息的机制。绑定分为前端通道绑定和后端通道绑定,前者使用用户的 web 浏览器进行消息传递(例如 HTTP-POST 或 HTTP-Redirect) ,后者使身份提供者和服务提供者直接通信(例如在 Artifact 绑定中使用 SOAP 调用)
Dscovery :
用于确定应该使用哪个身份提供程序来验证当前与服务提供程序交互的用户
Metadata :
描述一个或多个身份和服务提供者的文档。元数据通常包括实体标识符、公钥、端点 url、支持的绑定和配置文件以及其他功能或需求。身份和服务提供者之间的元数据交换通常是建立联合的第一步
Profile :
用于实现特定用例的协议、断言、绑定和处理指令的标准化组合,如单点登录、单点注销、发现、工件解析
Protocol :
为 SAML 消息定义格式(模式) ,用于实现特定功能,例如从 IDP 请求身份验证、执行单个注销或从 IDP 请求属性
Identity provider (IDP) 身份提供者(IDP) :
知道如何认证用户并使用联邦协议向服务提供者/中继方提供有关其身份的信息的实体
Service provider (SP) 服务供应商 :
您的应用程序与身份提供者通信,以获取与其交互的用户的信息。身份验证状态和用户属性等用户信息是以安全断言的形式提供的
Single Sign-On (SSO) 单点登录(SSO) :
允许访问多个网站的进程,而不需要重复提供身份验证所需的凭证。可以使用各种联邦协议(如 SAML、 WS-Federation、 OpenID 或 OAuth)来实现 SSO 用例。身份验证方式、用户属性、授权决策或安全令牌等信息通常作为单点登录的一部分提供给服务提供者
Single Logout (SLO) 单一登出(SLO) :
在使用单点登录访问的所有资源上处理终止身份验证会话。通常使用的技术包括将用户重定向到每个 SSO 参与者或发送注销 SOAP 消息
参考与感谢
讲道理 , 挺多的 ,但是记得时候又没有把地址记下来 , 不好找了 , 直接祝大家身体健康吧