なんとな~くしあわせ?の日記

「そしてそれゆえ、知識そのものが力である」 (Nam et ipsa scientia potestas est.) 〜 フランシス・ベーコン

SAML認証に関する自分なりのまとめ

SAML認証に関わるプログラミングをずっと続けていたので、ここらで少しわかりやすいまとめと考察を書きたい。

まず、SAMLはだいたい2種類に分けられる。SP-initiated SAMLIdP-initiated SAMLだ。

用語説明

で、これらがなぜ存在するかというと シングルサインオン - Wikipedia を実現するためだ。シングルサインオンは各Webサーバに同一のユーザIDとパスワードの組を入力する必要をなくさせるうえ、その安全を保証することを目的とする。このブログを読むようなGEEK層には無縁かもしれないが、世の中の大抵の人はID/Passを入力することに飽き飽きしている。

よって、

  • SP-initiated SAMLはSPを起点としたSAMLのシーケンス
  • IdP-initiated SAMLはIdPを起点としたSAMLのシーケンス

ということが自明になる。余談だが、SAMLの規格自体は2003年からある。どちらかというと若い規格かもしれない*1。というわけで、実際SAMLの規格を作ったOASISから図表を拝借してフローを見てみよう。<...>で囲まれた部分はSAMLで規定されたなんかのXMLだと思ってください。

SP-initiated SAML

5.1.2.SP-Initiated | Copyright © OASIS Open 2008. All Rights Reserved.

  1. ユーザーがSPにアクセス
  2. SPが<AuthnRequest>情報を含んでユーザーをIdPにリダイレクトする
  3. 認証情報をユーザーにリクエス
  4. ユーザーは何らかの方法(ID/Passなど)でログインする
  5. 電子署名されたXMLを作る
  6. ユーザーはそれをそのままPOSTする<Response>
  7. ユーザーはSPのWEBサイトにアクセスできる

f:id:panzer-jagdironscrap1:20160712230753g:plain

  • まあ結局IdPで一度認証しているんですけどね。これはSPが複数になっても成立するので、例えば1つのIdPのID/Passさえ覚えれば他のことは覚えないでいいわけです

IdP-initiated SAML

5.1.4 IdP-Initiated SSO: POST Binding | Copyright © OASIS Open 2008. All Rights Reserved.

  1. IdPが認証情報をユーザーにリクエス
  2. ユーザーは何らかの方法(ID/Passなど)でログインする
  3. ユーザーは接続するSPを選択
  4. 電子署名されたXMLを作る
  5. ユーザーはそれをそのままSPにPOSTする<Response>
  6. ユーザーはSPのWEBサイトにアクセスできる

f:id:panzer-jagdironscrap1:20160712230805g:plain

  • IdPが起点になったので<AuthnRequest>がなくなりました

SAMLXMLの中身

さて、ここからが本当に自分が書きたかったこと。SAMLXMLは何をどこで規定しているのか。

<AuthnRequest>

さっきのシーケンスから、<AuthnRequest>はSP → IdPに渡される情報だとわかったはずです。以下が<AuthnRequest>のサンプル:

<samlp:AuthnRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="identifier_1"
Version="2.0"
IssueInstant="2004-12-05T09:21:59Z"
AssertionConsumerServiceIndex="1">
<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>

IssuerやIssueInstanceもそれなりに重要なのですが、面白いのはNameIDPolicyです。ここでIdPが返すべきIDの種類やフォーマットが指定されます。*2 *3

SAMLのpdfを開きたくないので、Oracleのサイトを見ますと、以下のようなフォーマットが存在するようです。*4

  • urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
  • urn:oasis:names:tc:SAML:2.0:nameid-format:transient
  • urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
  • urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
  • urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
  • urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName
  • urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos
  • urn:oasis:names:tc:SAML:2.0:nameid-format:entity

楽なのはunspecifiedなのですが、emailAddressを指定してくるSPも結構あるようです。ここで、続く<Response>にて、異なるフォーマットを指定するとAuthnFailedになります。

<Response>

それでは次は<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:05Z"
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:05Z">
<saml:Issuer>https://idp.example.org/SAML2</saml:Issuer>
<!-- a POSTed assertion MUST be signed -->
<ds:Signature
xmlns:ds="http://www.w3.org/2000/09/xmldsig#">...</ds:Signature>
<saml:Subject>
<saml:NameID
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">
3f7b3dcf-1674-4ecd-92c8-1544f346baf8
</saml:NameID>
<saml:SubjectConfirmation
Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData
InResponseTo="identifier_1"
Recipient="https://sp.example.com/SAML2/SSO/POST"
NotOnOrAfter="2004-12-05T09:27:05Z"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions
NotBefore="2004-12-05T09:17:05Z"
NotOnOrAfter="2004-12-05T09:27:05Z">
<saml:AudienceRestriction>
<saml:Audience>https://sp.example.com/SAML2</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement
AuthnInstant="2004-12-05T09:22:00Z"
SessionIndex="identifier_3">
<saml:AuthnContext>
<saml:AuthnContextClassRef>
urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
</saml:Assertion>
</samlp:Response>
  • NameIDPolicy

一番大事なのは、<saml:Subject><saml:NameID>...</saml:NameID></saml:Subject>

先ほどのNameIDPolicyで指定された値をここのXMLの値として設定してHTTP POST要求をすると認証されます。中身は大抵WEBサービスのIDもしくはemailです。

  • X509証明書

あと、ここには書かれてませんが、以下のような形式でXML自体へのIdPの電子署名が入ります

<ds:Signature><ds:KeyInfo><ds:X509Data><ds:X509Certificate>
...署名...
</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature>

この署名が検証できなければ認証失敗します。SAMLアサーションが失敗するとかエラーメッセージがでるのはたいていこの辺です。

誰かの1秒のために3時間デバッグする

SAMLはほんとそんな感じです。慣れてくるとエラーの原因がなんとなくわかってくるのですが、それまでは呪文のようです。

便利なツールやドキュメントなど

SAMLの各種情報の解析ができます。SAMLのメッセージはエンコードされるので、それをデコードして見る必要があるんですね。提供しているのはoneloginです。オープンソースの世界でもoneloginはいろいろ提供してくれています(やはり自力が違う)。
www.samltool.com

上記をブラウザのプラグインで行えるツール
addons.mozilla.org