SAML認証に関する自分なりのまとめ
SAML認証に関わるプログラミングをずっと続けていたので、ここらで少しわかりやすいまとめと考察を書きたい。
まず、SAMLはだいたい2種類に分けられる。SP-initiated SAMLとIdP-initiated SAMLだ。
用語説明
- SPとはService Providerの略でWEBサービスの提供側を指す(例:Cybozu, Dropbox)
- IdPとはIdentity Providerの略でIDやIDのもつ情報の正当性を保証する側を指す(例:AWS, Google, Salesforce もしくはOpenAM, CA SiteMinderのようなIdPを構築できるツールやミドルウェア)
で、これらがなぜ存在するかというと シングルサインオン - Wikipedia を実現するためだ。シングルサインオンは各Webサーバに同一のユーザIDとパスワードの組を入力する必要をなくさせるうえ、その安全を保証することを目的とする。このブログを読むようなGEEK層には無縁かもしれないが、世の中の大抵の人はID/Passを入力することに飽き飽きしている。
よって、
ということが自明になる。余談だが、SAMLの規格自体は2003年からある。どちらかというと若い規格かもしれない*1。というわけで、実際SAMLの規格を作ったOASISから図表を拝借してフローを見てみよう。<...>で囲まれた部分はSAMLで規定されたなんかのXMLだと思ってください。
SP-initiated SAML
5.1.2.SP-Initiated | Copyright © OASIS Open 2008. All Rights Reserved.
- ユーザーがSPにアクセス
- SPが<AuthnRequest>情報を含んでユーザーをIdPにリダイレクトする
- 認証情報をユーザーにリクエスト
- ユーザーは何らかの方法(ID/Passなど)でログインする
- 電子署名されたXMLを作る
- ユーザーはそれをそのままPOSTする<Response>
- ユーザーはSPのWEBサイトにアクセスできる
- まあ結局IdPで一度認証しているんですけどね。これはSPが複数になっても成立するので、例えば1つのIdPのID/Passさえ覚えれば他のことは覚えないでいいわけです
IdP-initiated SAML
5.1.4 IdP-Initiated SSO: POST Binding | Copyright © OASIS Open 2008. All Rights Reserved.
- IdPが認証情報をユーザーにリクエスト
- ユーザーは何らかの方法(ID/Passなど)でログインする
- ユーザーは接続するSPを選択
- 電子署名されたXMLを作る
- ユーザーはそれをそのままSPにPOSTする<Response>
- ユーザーはSPのWEBサイトにアクセスできる
- IdPが起点になったので<AuthnRequest>がなくなりました
SAMLのXMLの中身
さて、ここからが本当に自分が書きたかったこと。SAMLのXMLは何をどこで規定しているのか。
<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のアサーションが失敗するとかエラーメッセージがでるのはたいていこの辺です。
便利なツールやドキュメントなど
- SAML online tools
SAMLの各種情報の解析ができます。SAMLのメッセージはエンコードされるので、それをデコードして見る必要があるんですね。提供しているのはoneloginです。オープンソースの世界でもoneloginはいろいろ提供してくれています(やはり自力が違う)。
www.samltool.com
- saml-tracer
上記をブラウザのプラグインで行えるツール
addons.mozilla.org