近期,两位安全研究人员,Klemen Bratec及 Ioannis Kakavas,公布了他们发现的一个在Microsoft Office 365平台上的SAML服务漏洞,该漏洞可被利用进行跨域认证绕过,最终可对 365平台上的所有联邦 域造成影响。 攻击者可以利用这个漏洞突破访问权限限制,越权获取到受害用户的Office 365账户信息,并可借此访问他们的邮箱以及存储在 OneDrive(微软的云存储服务)上的文件等等。目前该漏洞已经被微软临时修复。 SAML简介SAML即为安全声明标记语言,英文全称是Security Assertion Markup Language。它是一个基于XML的标准,用于在不同的安全域(security domain)之间交换认证和授权数据。其重要的作用在于跨域的单点登录。SAML实现的目标在于用户经过认证后,能对多个应用服务中的资源进行访问,无需重新进行身份验证(比如再次输入账户和密码等),而SAML便是这个过程中的“中间人”。 web单点登录(Single Sign-on,SSO)单点登录是一种用于方便用户访问网络的技术。如上所述,用户只需在登录时进行一次注册,即可获得访问系统和应用软件的授权,以后便可以在各类应用中切换,不必多次输入用户名和口令来确定身份。在此条件下,管理员无需修改或干涉用户登录就能方便地实施希望得到的安全控制。 例如,我们很多时候登录一些网站时,发现除了注册新账户外,还可以通过其他应用账户(如QQ、微博等)来进行登录,其中便会使用到WEB单点登录技术。因为从用户重复性的角度出发的话,实际上有些网站的用户是重复的,那么对于这部分用户来说,如何在访问网站A之后,无需验证地去访问网站B,web单点登录技术便是一个不错的解决方案。 SAML中若干重要的概念我们从前文简单了解了这一部分主要会详细关注在SAML 2.0的原理上。目前,SAML标准中最为重要的组成部分如下, 1、声明(Assertions) 首先,声明是一个XML结构,其中包含了打包在声明中的用户信息。其中两个最为常用的声明类型是: (1)认证声明(Authentication Assertions),包含用户已经对其身份进行认定的信息; (2)属性声明(Attribute Assertions),包含关于用户的特定信息(如邮件地址,名称等等)。 2、协议(Protocols) SAML协议描述了某些SAML要素(例如声明)是如何被封装在请求及系统是如何响应请求的,并给出了当进行登录或注销时,SAML实体(身份提供方和服务提供方)必须遵循的处理规则。也可以这么说,SAML协议定义了系统实体之间传递和处理SAML声明的协议集合,其中上述提到的验证请求协议将在之后介绍。 3、绑定(Bindings) SAML绑定描述了一个SAML消息如何被映射到非SAML相关的消息格式和通信协议中。例如,我们在进行身份验证过程中,服务提供方(即用户去访问的应用资源)需与身份提供方进行通讯验证,那么如何从传输的消息中,提取双方需要的信息,这个就涉及到了绑定。 当需要从一个HTTP GET请求中取出URL中的查询字符串时,HTTP重定向绑定(HTTP Redirect Binding )定义了如何对该URL进行格式化,使之符合 SAML消息标准格式 。而在通讯过程中,SAML请求是经过SAMLRequest查询参数进行传递的,并经过压缩后,再进行基于base64的编码和URL解码。 4、身份提供方(Identity Provider) 身份提供方经过SAML 授权,并持有着关于用户的信息, 身份提供方 可以为使用者发布声明,使其能够于 身份提供方 的应用上进行权限内的操作。 5、服务提供方(Service Provider) 服务提供方是SAML消息的接收者,其接受来自身份提供方的用户信息,并开放符合该用户访问权限的资源。 Web浏览器单点登录(SSO)的例子接下来可以看一个基于Web浏览器单点登录框架的简单例子,我们可以通过这个例子来更加深入理解SAML。首先,在进行单点登录的过程中,服务提供方使用 HTTP重定向绑定和身份提供方使用 HTTP POST绑定。其中整个过程涉及到的部分如下, 我们从下图中也可以看到关于三者之间的交互, 
一、在我们这个例子中,这个单点登录过程始于一个用户尝试访问一个受保护的资源(或者简单来说,可以理解为请求登录)。服务提供方拥有允许或者拒绝联合登录的功能配置,以及实时地将用户重定向到一个发现服务接口,以便来选择他们的身份提供方。通过自动匹配选择,可让服务提供方知道和信任所其选择的身份提供方,接着服务提供方会创建一个SAML身份验证请求,具体如下: <samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
ID="_bec424fa5103428909a30ff1e31168327f79474984" Version="2.0"
IssueInstant="2016-04-14T11:39:34Z" ForceAuthn="false"
IsPassive="false"
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
AssertionConsumerServiceURL="https://myserviceprovider.atsomeorg.com/Shibboleth.sso/SAML2/POST">
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
https://myserviceprovider.atsomeorg.com/shibboleth
</saml:Issuer>
<samlp:NameIDPolicy
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
AllowCreate="true" />
</samlp:AuthnRequest>
在请求的信息中,有两个重要部分需要了解下, 1、编码中的Issuer(发行人)代表了服务提供方的实体ID,其表现形式为一个 URI(统一资源标识符),像一串身份标识字符串一样的。Issuer(发行人)包含了服务提供方请求用户身份验证的信息。 2、IssueIstant,其包含的信息体现了当服务提供方发出请求后,生成的内部识别ID ,用以匹配在接受到该条请求后发出的SAML响应。 二、然后用户的浏览器被重定向到身份提供方绑定的URL,SAML Authentication Request(SAML 验证请求)是通过 HTTP GET中的一串查询参数(之后请求的信息会被进行压缩,再经过 base64以及URL编码再进行传递) 三、在接收来自用户浏览器的SAML请求后,身份提供方检查发送请求验证信息的服务提供方身份,并在成功认证服务提供方身份后,验证请求的内容,提示用户进行登录认证(输入账户密码)。如果用户认证成功,身份提供方将生成SAML回应,具体如下: <samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="_8e8dc5f69a98cc4c1ff3427e5ce34606fd672f91e6"
Version="2.0" IssueInstant="2016-04-14T11:40:48Z"
Destination="https://myserviceprovider.atsomeorg.com/Shibboleth.sso/SAML2/POST"
InResponseTo="_bec424fa5103428909a30ff1e31168327f79474984">
<saml:Issuer>http://idp.example.com/idp/shibboleth</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
ID="pfx65c8c6cd-b03b-8634-fd54-636fa66e7722"
Version="2.0" IssueInstant="2016-04-14T11:40:48Z">
<saml:Issuer>http://idp.example.com/idp/shibboleth</saml:Issuer>
<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/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#pfx65c8c6cd-b03b-8634-fd54-636fa66e7722">
<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/2000/09/xmldsig#sha1"/>
<ds:DigestValue>gxnHkIizISbLkkB1vSWapmWuQzk=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>Qn69P4a3PQTISfqk/0t2JdJqG1nlswFQt8bNWPZ+K41EIYkCcTyuwlKnCzlTvU1YgNXIvHcFEyKjYAge+s3gwqecATI+yRB9OtD34YxBC4kyGcbq/ETQxIQ515xehfRxLrQjUpRzgHQXMLSjGdgjeelfKsHeSczA9Hp44kasQSs=</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>MIICajCCAdOgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBSMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRcwFQYDVQQDDA5zcC5leGFtcGxlLmNvbTAeFw0xNDA3MTcxNDEyNTZaFw0xNTA3MTcxNDEyNTZaMFIxCzAJBgNVBAYTAnVzMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxPbmVsb2dpbiBJbmMxFzAVBgNVBAMMDnNwLmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZx+ON4IUoIWxgukTb1tOiX3bMYzYQiwWPUNMp+Fq82xoNogso2bykZG0yiJm5o8zv/sd6pGouayMgkx/2FSOdc36T0jGbCHuRSbtia0PEzNIRtmViMrt3AeoWBidRXmZsxCNLwgIV6dn2WpuE5Az0bHgpZnQxTKFek0BMKU/d8wIDAQABo1AwTjAdBgNVHQ4EFgQUGHxYqZYyX7cTxKVODVgZwSTdCnwwHwYDVR0jBBgwFoAUGHxYqZYyX7cTxKVODVgZwSTdCnwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQByFOl+hMFICbd3DJfnp2Rgd/dqttsZG/tyhILWvErbio/DEe98mXpowhTkC04ENprOyXi7ZbUqiicF89uAGyt1oqgTUCD1VsLahqIcmrzgumNyTwLGWo17WDAa1/usDhetWAMhgzF/Cnf5ek0nK00m0YZGyc4LzgD0CROMASTWNg==
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
<saml:Subject>
<saml:NameID
SPNameQualifier="https://myserviceprovider.atsomeorg.com/shibboleth"
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">
_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7
</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData
NotOnOrAfter="2016-04-14T11:50:48Z"
Recipient="https://myserviceprovider.atsomeorg.com/Shibboleth.sso/SAML2/POST"
InResponseTo="_bec424fa5103428909a30ff1e31168327f79474984"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="2016-04-14T11:40:48Z"
NotOnOrAfter="2016-04-14T11:50:48Z">
<saml:AudienceRestriction>
<saml:Audience>https://myserviceprovider.atsomeorg.com/shibboleth</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2016-04-14T11:40:48Z"
SessionNotOnOrAfter="2016-04-14T11:50:48Z"
SessionIndex="_be9967abd904ddcae3c0eb4189adbe3f71e327cf93">
<saml:AuthnContext>
<saml:AuthnContextClassRef>
urn:oasis:names:tc:SAML:2.0:ac:classes:Password
</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute Name="uid"
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">test</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="mail"
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">[email protected]</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="eduPersonAffiliation"
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">users</saml:AttributeValue>
<saml:AttributeValue xsi:type="xs:string">examplerole1</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
</samlp:Response>
四、接下来,引导用户浏览器回应一个HTTP POST请求到服务提供方,在本次案例中假设为https://myserviceprovider.atsomeorg.com/Shibboleth.sso/SAML2/POST 。其中,有部分内容也是需要了解的,具体如下。 1、InResponseTo,包含了一个ID值,该ID会与先前服务提供方发送请求的时候产生的内部标识ID进行匹配(避免重放攻击);
2、IssueIstant, NotBefore 以及NotOnOrAfter 定义了 SAML响应(以及声明)的有效时间,也是为了避免重放攻击; 3、声明还包含了一个 Isssuer字段,其中具有一个 url,因此服务提供方可以确认身份提供方的身份(是否符合此前请求指定的身份提供方地址); 4、AudienceRestriction部分,则定义了可对该声明进行读取的服务提供方,而其他服务提供方并无权限进行读取。 5、Subject,用于识别验证用户身份; 6、AttributeStatement部分,包含用户进行验证的信息,其中也包括各种属性以及它们的值; 在这份声明中(也包括全部的 SAML响应)是基于XML签名( XML Signature )来保护其完整性,检查其在传输过程中并未遭受篡改。
五、在接收到SAML响应后,服务提供方可以验证其内容和结构,并验证签名,随后通过了用户认证,分配用户一个Cookie,启动与用户间的Web会话。 问题在哪里?那么到这里,或许你会有些疑惑, 1、服务提供方如何知道并信任身份提供方? 2、身份提供方如何知道并信任服务提供方? 3、身份提供方如何对声明进行签名? 4、服务提供方在接受到一份声明后如何验证其完整性? 实际上,在验证之前,身份提供方和服务提供方会进行相互之间的信任处理。为了实现在请求和响应的交互过程中,身份及信息的验证,两者之间需要交换元数据。我们可以理解为,此处的元数据包含了一个公钥,对应着身份提供方对声明进行签名加密所使用的私钥,每一个实体(可以是用户、服务提供方或是身份提供方)所绑定的URL以及支持或响应的算法等等。那么基于这样的出发点,有两种方法可让他们获取彼此的元数据: 1、双方以一种安全的方式交换元数据,确立信任关系; 2、或者通过加入一个联邦(Federation),将这种信任关系委托給第三方机构。然后联邦运营者会设定任务,收集来自所有参与的实体(身份提供方和服务提供方)的元数据,并发布这些数据。每个身份提供方和服务供应方都会提交其元数据来获取有关参与该联邦的其他实体的信息。 Office 365 的SAML交互是如何运行的?Office 365平台服务提供方使用的是一个WS-Trust以及 SAML 2.0 web 浏览器单点登录框架的混合体。Office 365平台支持通过WS-Trust以及SAML 2.0 web 浏览器进行单点登录两种方式进行访问,但两者之间并不孤立。
实际上,WS-Trust为我们提供了一种安全令牌服务,比如令牌的颁发(Issuance)、续订(Renewal)和终止(Cancel)等。但SAML的服务提供方会将SAML 2.0标准的传输信息,使用令牌( token)转换服务,在内部将 SAML信息转化为WS-Trust信息。而因为上述的令牌转换服务,所以此次在 SAML服务提供方发现的漏洞,也会影响到WS-Trust单点登录。
然而,为了简单起见,在理解本文Office 365 的服务提供方时,均可认为是使用SAML进行单点登录。
不过有一点需要强调的是, 对于经过SAML的账户验证,Office 365并不支持实时资源配置,所以对于使用账户进行单点登录的用户,需要已经在Azure AD上有租户才行。这个需要经过Directory同步或者经过用户在IDM 系统的帮助下进行配置,不过这已经超出了本文的范围,这里先略过不谈。
从一个身份提供方到 Office 365服务提供方,需要在请求消息中发布的属性,以便进行信息匹配,包括以下两个:
1、UPN(User principal name)用户主体名称,包含IDP(即身份提供方)Email名称等信息; 2、ImmutableId,用户的唯一识别符,存放于SAML声明的Subject中。 接下来,我们可以看下Office 365平台的登录情况, 流程始于用户开始访问 Office 365 portal ,接着被重定向到https://login.microsoftonline.com/login.srf ,而响应形式如下, 
在键入用户名称以及按下“TAB”或点击密码输入区域,该页面将构建一个XHR (XMLHttpRequest)发送到https://login.microsoftonline.com/common/userrealm ,而为了验证用户的域(可以简单理解为用户归属的企业)是否能与Office 365平台上的租户(可以理解为某企业所租赁的服务)相匹配。 GET /common/userrealm/[email protected]&api-version=2.1&stsRequest=rQIIAbNSzigpKSi20tcvyC8qSczRy09Ly0xO1UvOz9XLL0rPTAGxioS4BMruuVuZ2Fh77Wj-e6KxLMF2FaMaTp36OYl5KZl56XqJxQUVFxgZu5hYDA2MjTcxsfo6-zp5nmCacFbuFpOgf1G6Z0p4sVtqSmpRYklmft4jJt7Q4tQi_7ycypD87NS8Scx8OfnpmXnxxUVp8Wk5-eVAAaDxBYnJJfElmcnZqSW7mFVSU00tTCxTUnRNkpOTdU2Sksx0kwxSzXRTzZMtTC1ME00Mk1MOsGwIucAi8IOFcREr0C-3A6ZLrn182Gt-tWV-vVlpwi5OW-L8Yl-SWJSeWmKrapSWkpqWWJpTAhYGAA2&checkForMicrosoftAccount=false HTTP/1.1
Host: login.microsoftonline.com
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0
Accept: application/json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
DNT: 1
X-Requested-With: XMLHttpRequest
Referer: https://login.microsoftonline.com/login.srf?wa=wsignin1.0&rpsnv=4&ct=1460721662&rver=6.7.6640.0℘=MCMBI&wreply=https%3a%2f%2fportal.office.com%2flanding.aspx%3ftarget%3d%252fdefault.aspx&lc=1033&id=501392&msafed=0&client-request-id=3a47de76-3c34-4a3b-b883-fdc88176603d
如果域已被知道及配置成联邦身份,用户的浏览器将被引导构建一个 HTTP POST请求到 HTTP-POST绑定的身份提供方的 URL,具体的信息如下, <samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="_f6daef39-fb54-407e-abb4-c75d261b75ae"
IssueInstant="2016-04-11T21:13:44Z"
Version="2.0"
AssertionConsumerServiceIndex="0"
>
<saml:Issuer>urn:federation:MicrosoftOnline</saml:Issuer>
<samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" />
</samlp:AuthnRequest>
用户实时在身份提供方中进行身份验证,如下, 
随后浏览器会被引导到创建一个HTTP POST返回到 Office 365 HTTP-POST绑定的 URL,这里为 https://login.microsoftonline.com/login.srf 。通过包含着声明的SAML响应。一个简单的响应例子如下, <saml2p:Response Destination="https://login.microsoftonline.com/login.srf"
ID="_cefc5f992f2e455d7b3e52522fc479db"
InResponseTo="_f6daef39-fb54-407e-abb4-c75d261b75ae"
IssueInstant="2016-04-11T21:14:35.365Z" Version="2.0"
xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
https://idp.admin.grnet.gr/idp/shibboleth
</saml2:Issuer>
<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/2000/09/xmldsig#rsa-sha1" />
<ds:Reference URI="#_cefc5f992f2e455d7b3e52522fc479db">
<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#">
<ec:InclusiveNamespaces
PrefixList="xsd"
xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<ds:DigestValue>dc1f3jn97lZ6FWdxGxsEWxXNsTM=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
Removed for brevity
</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>
Removed for brevity
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
<saml2p:Status>
<saml2p:StatusCode
|