SAML (Security Assertion Markup Language)入門
提到SAML (Security Assertion Markup Language), 很多人都會聯想到單點登入SSO。那麼Saml到底是什麼,它跟sso到底有什麼聯系?這裡給大家分享一下我在讀完了saml差不多全部規範之後的一些心得。希望給saml入門者一些幫助。 我并不想詳細介紹每個xml節點怎麼寫。大家可以參考标準規範。 看了這篇随筆,相信如果萬一哪天你要做saml, 你也不會害怕了。
Saml是什麼
首先,saml是一種xml格式的語言。 翻譯過來大概叫 安全斷言(标記)語言。 這裡有兩個點: 第一是“安全”, 第二是“斷言(assertion)”。 用人話翻譯saml就是 用安全的方式表達斷言一種語言。
先看它的核心概念“斷言”。 斷言是什麼? 就是做出判斷的語言。比如一句話: 小明是超級管理者。 這就是一個斷言。再來一個例子:小紅沒有權限讀取根目錄。這也是一個斷言。 這種“做出判斷的語句”我們在很多場合都需要用到。 比如你在網上嘗試登陸一個服務的時候, 這個服務需要知道你是不是合法的使用者。 這個時候如果你能提供一個“安全,可靠,可信任”的斷言:“小明有權登陸XX服務”, 那麼這個服務就知道你合法了, 于是就能為你提供服務了。 這個例子比較抽象,但基本上能表達斷言在實際用例中的作用了。 實際上saml的大部分用例就在于證明你是誰,你擁有什麼權限等等了。 saml中大部分主要内容也都是類似于:你是誰, 你有什麼。。等等這些簡單的語句。 詳細内容後面會介紹。
接下來第二個概念就是“安全”了。 你能提供一個斷言, 别人能不能假冒你提供一個斷言進而騙取服務端的信任呢? 另外服務端為什麼會信任你給的斷言呢? 這就涉及到安全的問題了。為了防止斷言被假冒,篡改。saml中加入了安全措施。 當然現今能抵禦假冒,篡改,重播攻擊的利器就是公鑰-私鑰系統了。 通過給斷言加上簽名和加密,再結合數字證書系統就確定了saml不受攻擊。
在很多sso的場合中, 都支援saml登陸。 這就是saml最多的一個應用場景。 作用相當于大家熟知的OpenID,和Oauth等等。
好了,說完了大體的概念,就來程式員最喜歡的硬菜了。
從技術的角度看saml。
saml迄今為止有兩個廣泛應用的标準, Saml 1.1 和Saml 2.0
為了嘗鮮,大家先看兩個saml的例子, 看個樣子即可,不用閱讀内容,給你1分鐘, 看完趕緊回來接着看這裡哦:
http://en.wikipedia.org/wiki/SAML_1.1
http://en.wikipedia.org/wiki/SAML_2.0
恩,很好, 你已經知道saml大概長什麼樣了。 saml1.1和saml2.0 是同一個标準的兩個版本, 他們在邏輯概念或者對象結構上大緻相當, 隻是在一些細節上有所差異。 這兩個版本不相容。 另外1.1比2.0要簡單許多。 是以下面在講邏輯結構的時候一般不區分這兩個版本,除非特别說明的地方。
我猜你一定喜歡下面這種圖:
這張圖取自: https://www.oasis-open.org/committees/download.php/11511/sstc-saml-tech-overview-2.0-draft-03.pdf
這是saml2.0的一個極其簡單的應用場景. 如果你不嫌煩的話,我來解釋一下這個圖:
圖上共有三個角色, 1,SP, 服務提供者。 2, Idp,認證使用者并生成斷言。 3,就是使用者你了, client。
首先, 你(client)是idp的注冊使用者, 它有你的使用者名和密碼,它可以認證你就是你。 其次, SP和Idp兩者會被各自的域管理者設定為互相信任對方。并且雙方都持有對方的公鑰。這是配置好的。第三,有一天,你需要通路sp提供的某個服務,但是sp并不認識你,也沒有你的使用者名和密碼是以不能認證你。 于是就發生了上圖所示的8個步驟:
1. 你去通路sp的某個受保護資源,比如浏覽器打開: http://www.apc.com/resource1.aspx.
2. sp發現你是新來的,沒有認證資訊。當然不能給你這個頁面内容了。 他就會生成一個 saml的認證請求資料包(當然是saml格式的)。把這個請求放在一個html的form的一個隐藏的域中,把這個html form傳回給你。 這個form後面有一句javascript自動送出這個form。 二而form的action位址就是 提前配置好的 idp上的一個位址。
saml認證請求的資料包可能是這個樣子的:
==========
<samlp:AuthnRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="aaf23196-1773-2113-474a-fe114412ab72"
Version="2.0"
IssueInstant="2004-12-05T09:21:59"
AssertionConsumerServiceIndex="0"
AttributeConsumingServiceIndex="0">
<saml:Issuer>https://sp.example.com/SAML2</saml:Issuer>
<samlp:NameIDPolicy
AllowCreate="true"
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"/>
</samlp:AuthnRequest>
==========
而傳回的html from内容大概設這個樣子的:它包含了上面的資料包作為其中一個hidden的值。
=============================
<form method="post" action="https://idp.example.org/SAML2/SSO/POST" ...>
<input type="hidden" name="SAMLRequest" value="<samlp:AuthnRequest>.......... </samlp:authnreques>" />
... other input parameter....
<input type="submit" value="Submit" />
</form>
<javascript>
document.form[0].submit();// 後面緊跟一句類似這樣的送出代碼.
</javascript>
=============================
這些代碼一部分是複制過來的, 有些是我現寫的, 大家領會意思即可,不要在意那些細節。
3. 上面的form會被javascript自動送出到idp的某個位址。
4. idp也需要認證你, 于是傳回給你一個認證的頁面, 可能使用使用者名密碼認證,也可以使用ntlm認證等等一切可以認證你的方式。 因為idp儲存有你的使用者名和密碼。
5. 同上一步,也是認證你的一個過程。
6. idp在認證你之後。覺得你合法, 于是就為你生成一些斷言, 證明你是誰,你有什麼權限等等。 并用自己的私鑰簽名。 然後包裝成一個response格式,放在form裡傳回給你。
斷言的格式大概如下:
=============
<saml:Assertion
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
ID="b07b804c-7c29-ea16-7300-4f3d6f7928ac"
Version="2.0"
IssueInstant="2004-12-05T09:22:05">
<saml:Issuer>https://idp.example.org/SAML2</saml:Issuer>
<ds:Signature
xmlns:ds="http://www.w3.org/2000/09/xmldsig#">...</ds:Signature>
<saml:Subject>
..........
</saml:Subject>
<saml:Conditions
.........
</saml:Conditions>
<saml:AuthnStatement
AuthnInstant="2004-12-05T09:22:00"
SessionIndex="b07b804c-7c29-ea16-7300-4f3d6f7928ac">
<saml:AuthnContext>
<saml:AuthnContextClassRef>
urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute
xmlns:x500="urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500"
x500:Encoding="LDAP"
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.1"
FriendlyName="eduPersonAffiliation">
<saml:AttributeValue
xsi:type="xs:string">member</saml:AttributeValue>
<saml:AttributeValue
xsi:type="xs:string">staff</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
=============
其中authnstatement認證語句表示你認證成功了。subject表示你是誰。而attributestatement表示你有哪些屬性。 還有一個授權語句上面例子中沒有。
Response語句大概如下:
============================
<samlp:Response
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="identifier_2"
InResponseTo="identifier_1"
Version="2.0"
IssueInstant="2004-12-05T09:22:05"
Destination="https://sp.example.com/SAML2/SSO/POST">
<saml:Issuer>https://idp.example.org/SAML2</saml:Issuer>
<samlp:Status>
<samlp:StatusCode
Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<saml:Assertion
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="identifier_3"
Version="2.0"
IssueInstant="2004-12-05T09:22:05">
<saml:Issuer>https://idp.example.org/SAML2</saml:Issuer>
<!-- a POSTed assertion MUST be signed -->
....................
</saml:Assertion>
</samlp:Response>
============================
正如上面第2步一樣,它也會把response包裝在一個form裡面傳回給你,并自動送出給 sp的某個位址。
===========
<form method="post" action="https://sp.example.com/SAML2/SSO/POST" ...>
<input type="hidden" name="SAMLResponse" value="<samlp:Response>.........</samlp:respons>" />
<input type="hidden" name="RelayState" value="''token''" />
...
<input type="submit" value="Submit" />
</form>
<javascript>
document.form[0].submit();// 後面緊跟一句類似這樣的送出代碼.
</javascript>
===========
7. 于是就到了第7步, 這個form被javascript自動送出到sp了。
8. sp讀到form送出上來的 斷言。 并通過idp的公鑰驗證了斷言的簽名。 于是信任了斷言。 知道你是idp的合法使用者了。 是以就最終給你傳回了你最初請求的頁面了。 http://www.apc.com/resource1.aspx.
好了一個最簡單的saml用例就講完了。 你可以看到其中幾乎所有的步驟都可以自動完成,使用者在第一步通路資源之後,就看到浏覽器再自動跳轉,自己不需要操作什麼,幾秒鐘過後,資源就通路成功了。
到這裡, 相信saml在你心目中的形象一定跟家立體了。 如果你還有興趣就繼續往下看吧.
上面是“遠觀”, 下面我們走近。
先看saml标準的結構:
此圖出自: https://www.oasis-open.org/committees/download.php/11511/sstc-saml-tech-overview-2.0-draft-03.pdf
saml标準從内到外 可以分為上圖的4個層次:
1. Assertion。 斷言。 規定了斷言的xml結構, 例如:
==============
<saml:Assertion>
..............
</saml:Assertion?
==============
它規定了,這個assertion節點到底該怎麼寫, 其實就是這個節點的schema。 按照這個規定寫出來的assertion别人才能認識。
2. Protocols。協定。它規定了如何請求(samlrequest)和回複(samlresponse )saml消息,其中當然包含assertion的消息。比如:
===============
<samlp:AuthnRequest>
............
</samlp:AuthnRequest>
還有:
<samlp:Response>
..............
</samlp:Response>
===============
它規定了怎麼發送這些請求消息,和回複消息的結構。 這樣sp,idp之間才能通信。
3. 綁定。 上面兩點都是規定了靜态結構。 具體這些消息怎麼發送呢。 就是用什麼協定來承載這些smal消息呢。就是綁定出馬了。 最常用的就是http或者soap消息。 把上面的saml消息通過http或者soap消息來傳輸。 這樣sp和idp就能通信了。 saml1.1隻支援 http的soap綁定。 而saml2.0支援更多的綁定。 有興趣自己閱讀标準。 這裡需要強調的是, 你可能已經想到了,那就是這個綁定其實不重要。 隻要saml消息本身是完整的可靠的,下層用什麼協定傳輸不重要。 對。 saml标準規定的綁定隻是一種标準實作。 saml的消息可以綁定到任何協定上, 隻要sp和idp實作協商好就行了。 這裡面應用最廣泛的恐怕要算saml的wss綁定了。 用在微軟的一系列産品裡面。 包括sharepoint online的登陸授權, windows azure登陸,以及windows store的登陸授權等等。 微軟自己在ws-trust和ws-secure協定上傳輸了saml消息。 這恐怕是saml标準以外用的最多的綁定了。
4. Profile, 這個單詞我實在不知道翻譯成啥好,是以就寫原文把。 我個人喜歡把它叫做一套配置,或者叫解決方案。 它規定了某些場景下一整套saml認證的細節和步驟。 比如, 它規定了比較著名的SSO方案。 就是如何用saml實作sso的一整套配置和詳細步驟。 概念就是這樣。 同上, 上面的綁定都不确定,是以這個profile就更自由了。 你可以使用任何自己定義的profile,隻要你們自己協商好就行了。
恩好吧, 先到這吧。大體結構已經出來了。 後續我可能會再分享一下有趣或者有坑的地方。 歡迎大家通路我的個人獨立部落格交流學習: http://byNeil.com
附上saml協定标準位址:
https://www.oasis-open.org/committees/download.php/3406/oasis-sstc-saml-core-1.1.pdf
http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
參考:
https://www.cnblogs.com/shuidao/p/3463947.html
https://blog.csdn.net/csethcrm/article/details/20694993
https://blog.csdn.net/jenyzhang/article/details/52933280
http://www.cnblogs.com/qiuhk/p/9387772.html