#目的
認証/認可について少しづつですが備忘録としてまとめようと思います。
今回はSaaS連携で欠かせないものとなっているSAMLのSSOについてです。
まだまだ学び始めなので、誤りなどございましたら、ご指摘いただけますと幸いです。
認証/認可、基礎的なSSOの知識があることを前提としています。
#SAML概要
Security Assertion Markup Language(SAML)とは、 OASISで標準として策定されたマークアップ言語です。主にSSOやID連携で利用されています。
2005年3月にSAML2.0が策定され、これが最新版となっています。
OASIS SAML Wiki
https://wiki.oasis-open.org/security/FrontPage
##SAMLを利用したSSO
SAML形式のSSOでは、アサーションと呼ばれるユーザの認証情報、属性、ユーザへの権限の認可といったセキュリティ情報をXML形式で記述したものを認証サーバー(IdP)からサービスプロバイダー(SP)に送って、それをSPで検証することで認証の仕組みが実現されます。
これにより、社内ネットワークに存在する認証サーバー(IdP)の認証情報を使って、社外のSaaS(SP)などに安全にログインすることができます。
SP始まりのSSOのフローは大体こんな感じです。点線部は、ログイン済みの場合SKIPされます。
上記フローを見ていきます。
事前にユーザーがIdPにアカウントが作成されているとします。
①Http Request to SP
ユーザーからSPにリクエストを送ります。
②SP Determines IdP
SPは認証要求を送るIdPを特定した後、認証要求(<AuthnRequest?>
)を発行し、IdPに送信します。この要求方法をBindingsと言います。事前にどのようなBindingsで認証要求を送るかIdPとSP間で認識しておく必要があります。
③④IdP identifies Principal
ユーザーがIdPにログインします。
⑤IdP issues Response to SP
アカウントの認証が終了した後、IdPはアサーションかエラーをSPに送信します。この認証結果を返す方法も、②と同様にBindindsと呼ばれ、こちらも事前にIdPとSP間で同意していく必要があります。
⑥SP grants or denies access to Principal
ユーザーは適切なSPのリソースへとアクセスすることができます。
上記のフローにも記載がある通り、SAMLでは、SPとIdPが直接やり取りを行わないので、社内サーバーと社外SaaS間で通信を確立せずに、SSOが実現できるメリットがあります。
SPとIdP間でやり取りしているSAMLの仕様について次のセクションで詳しく見ていきます。
#SAMLの仕様
SAMLの標準仕様には、アサーションの詳細と、アサーションを伝送するためのプロトコルとに関する文法と意味論を定義しています。SAML標準仕様の中でWeb Browser SSO ProfileにのっとったSSOを実装する上で重要となる以下3つをみていきます。
-
Bindings
- SAMLアサーションと要求/応答プロトコル・メッセージを交換する方法を定義
- SAMLメッセージを実際の通信プロトコル(HTTPなど)にマッピングする方法を定義
-
Core(Assertions and Protocols)
- SAMLアサーションを作成するための構文とセマンティクスを定義
-
Metadata
- SPとIdP間で設定情報を表現および共有するXMLのスキーマを定義
今回詳しく紹介はしませんが、以下3つも参考になります。
Technical OverviewにはSAMLの技術概要が載っています。概要よりも細かいSAML連携の具体的な流れはProfilesに載っているので、こちらで実際の連携パターンと照らし合わせて仕様の取捨選択が可能になっています。よくわからない単語があれば用語集(SAMLGloss)を確認するといいかもしれません。
##Bindings
SAML 要求/応答メッセージ交換を標準メッセージングまたは通信プロトコルにマッピングすることはSAMLプロトコルバインディング(または単にバインディング)と呼ばれ、
中でも、特定の通信プロトコル<FOO>
へのマッピングインスタンスをSAMLの<FOO>
バインディングまたは、SAML<FOO>
バインディングと言います。
例えば、SAML SOAPバインディングは、SAML要求/応答メッセージ交換をSOAPメッセージ交換にマッピングする方法を記述します。
ここで、バインディングのSAML 要求/応答メッセージとは、samlp:RequestAbstractType型およびsamlp:StatusResponseType型から派生した任意のSAMLプロトコルメッセージを指します。
(メッセージの詳細に関してはCoreを参照してください)
###セキュリティ
クライアントがSSL 3.0 または TLS 1.0 を使用する際、
サーバはX.509 v 3証明書を使用します。
セキュリティ項目は、特に明記がない限り、全てのBindingsに適用されます。
###種類
主に利用される2つのバインディングを紹介します。
####HTTP Redirect Binding
SAML Protocol メッセージをURLパラメタで送信できるメカニズムを定義しています。
URLパラメタでメッセージを送信するには特殊エンコードが必要です。また、文字制限もあるため、メッセージが小規模な場合に適しています。
SAML要求で、よく使用されているイメージがあります。
例:
SPからIdPへSAML要求メッセージを送る場合(SAMLフローの②)
まずSPは、署名なしの<samlp:AuthnRequest>型
のXMLメッセージを作成します。
(AuthnRequest型はRequestAbstractType型からの派生)
メッセージの詳細については、Coreで取り扱います。
oneloginさんでは下記のような例が記載されています。
https://www.samltool.com/generic_sso_req.php
saml:Issuerhttp://sp.example.com/demo1/metadata.php
saml:AuthnContextClassRefurn:oasis:namesSAML:2.0classes:PasswordProtectedTransport
作成した要求メッセージを[DEFLATE圧縮→Base64→URLエンコードの順でエンコード](https://techinfoofmicrosofttech.osscons.jp/index.php?SAML%20Bindings#t5ca2a0a)して、URLパラメタとしてidpのエンドポイントに渡します。(HTTP Redirectを利用してリクエストを投げる)
※下記例は、実際にエンコードしたものではなく、Wikipediaに例として載っていたものです。
```URI
https://idp.example.org/SAML2/SSO/Redirect?SAMLRequest=fZFfa8IwFMXfBb9DyXvaJtZ1BqsURRC2Mabbw95ivc5Am3TJrXPffmmLY3%2FA15Pzuyf33On8XJXBCaxTRmeEhTEJQBdmr%2FRbRp63K3pL5rPhYOpkVdYib%2FCon%2BC9AYfDQRB4WDvRvWWksVoY6ZQTWlbgBBZik9%2FfCR7GorYGTWFK8pu6DknnwKL%2FWEetlxmR8sBHbHJDWZqOKGdsRJM0kfQAjCUJ43KX8s78ctnIz%2Blp5xpYa4dSo1fjOKGM03i8jSeCMzGevHa2%2FBK5MNo1FdgN2JMqPLmHc0b6WTmiVbsGoTf5qv66Zq2t60x0wXZ2RKydiCJXh3CWVV1CWJgqanfl0%2Bin8xutxYOvZL18NKUqPlvZR5el%2BVhYkAgZQdsA6fWVsZXE63W2itrTQ2cVaKV2CjSSqL1v9P%2FAXv4C
リクエストする際に、AuthnRequestをキャッシュされないように
Pragma: no-cache Cache-Control: no-cache, no-store
をつけておきます。
署名などもIdPから要求されていた場合、さらにURLパラメタにSigAlgやSignature、RelayStateも追加します。(各値のエンコードも忘れずに)
?SAMLRequest=...&RelayState=...&Signature=...
こんな感じ。
ちなみに、SigAlgは署名アルゴリズム識別子[XMLSig]のURI表現で、RelayStateはOIDCで言うところのリダイレクトURI(認証完了後にユーザーがアクセスするURL+アクセス先の情報がある場合もある)みたいなものらしいです。
RelayStateに関しては、パラメタに含める方法の他にも、事前に登録しておく方法もあるようです。
####HTTP POST Binding
HTMLのformコントロールでプロトコルメッセージを転送する方法を定義しています。
formコントロールでは以下のように属性を定めてリクエストを送ります。submit方法は定められていません。(任意の方法を利用可能)
- method属性 : POST
- action属性 : 配信先HTTPエンドポイント
- name属性
- SAMLRequest(SAML Requestの場合)
- SAMLResponse(SAML Responseの場合)
HTTP POST Bindingにおいても、メッセージのエンコードが必要です。
HTTP Redirect BindingではメッセージをDEFLATE圧縮→Base64→URLエンコードの順でエンコードしていましたが、POST Bindingでは、Base64エンコードのみでOKです。
HTTP Redirect Bindingと比べて、長いメッセージを送ることが可能で、HTTPの Refererヘッダや、HTTPログに公開される可能性がない特徴を持つことからSAML応答でよく使用されているイメージがあります。
また、メッセージに署名を含めることができます。この場合は、Root SAML要素のDestination XML属性に、メッセージの送り先のURLを指定する必要があります。例えば、先ほどのoneloginさんの例だと、SPからのAuthnRequestではIdPのSAMLエンドポイントが指定されていますね。
またRelayStateの値が書き換えられないように、RelayStateにも個別に署名をつける必要もあるようです。こちらの詳細については、見当たらなかったので、どのような意味なのかもしご存知の方いらっしゃいましたらご指摘お願いいたします。
##Core(Assertions and Protocols)
SAMLアサーションとメッセージ交換(Bindings)で用いられるXMLのスキーマを定義しています。
###Assertions
アサーションはSP(リライング・パーティ)からの要求によって、IdP(アサーティング・パーティ)がユーザー(プリンシパル)の認証結果についてXML形式で記述したものです。
アサーションは、通常、
- subject(
<saml:Subject>
) - 検証条件(
<saml:Conditions>
) - ステートメント(
<saml:Statement>
)
で、構成されます。
このアサーションはSAML応答に含まれます。
実際のSAML応答をsalesforceさんで公開されている例で見てみます。
こちらについては、非常に細かい内容なので、ざっと概要だけコメントしてあります。
接頭辞samlpはSAMLプロトコル名前空間、samlはアサーション名前空間を示しています。
<samlp:Response ID="_257f9d9e9fa14962c0803903a6ccad931245264310738"
IssueInstant="2009-06-17T18:45:10.738Z" Version="2.0">
<saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">
https://www.salesforce.com
</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<!-- アサーションここから -->
<!-- 属性について ID:400 ビット以内のID, IssueInstant:発行時刻を指定, Version:バージョンを指定 -->
<saml:Assertion ID="_3c39bc0fe7b13769cab2f6f45eba801b1245264310738"
IssueInstant="2009-06-17T18:45:10.738Z" Version="2.0">
<!-- saml:IssuerはIdPのURI -->
<saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">
https://www.salesforce.com
</saml:Issuer>
<!-- saml:SignatureはXML署名 -->
<saml:Signature>
<saml:SignedInfo>
<saml:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<saml:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<saml:Reference URI="#_3c39bc0fe7b13769cab2f6f45eba801b1245264310738">
<saml:Transforms>
<saml:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<saml:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="ds saml xs"/>
</saml:Transform>
</saml:Transforms>
<saml:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<saml:DigestValue>vzR9Hfp8d16576tEDeq/zhpmLoo=
</saml:DigestValue>
</saml:Reference>
</saml:SignedInfo>
<saml:SignatureValue>
AzID5hhJeJlG2llUDvZswNUrlrPtR7S37QYH2W+Un1n8c6kTC
Xr/lihEKPcA2PZt86eBntFBVDWTRlh/W3yUgGOqQBJMFOVbhK
M/CbLHbBUVT5TcxIqvsNvIFdjIGNkf1W0SBqRKZOJ6tzxCcLo
9dXqAyAUkqDpX5+AyltwrdCPNmncUM4dtRPjI05CL1rRaGeyX
3kkqOL8p0vjm0fazU5tCAJLbYuYgU1LivPSahWNcpvRSlCI4e
Pn2oiVDyrcc4et12inPMTc2lGIWWWWJyHOPSiXRSkEAIwQVjf
Qm5cpli44Pv8FCrdGWpEE0yXsPBvDkM9jIzwCYGG2fKaLBag==
</saml:SignatureValue>
<saml:KeyInfo>
<saml:X509Data>
<saml:X509Certificate>
MIIEATCCAumgAwIBAgIBBTANBgkqhkiG9w0BAQ0FADCBgzELM
[Certificate truncated for readability...]
</saml:X509Certificate>
</saml:X509Data>
</saml:KeyInfo>
</saml:Signature>
<!-- saml:Subjectここから -->
<saml:Subject>
<!-- Subjectの識別子を指定、NameIDの他にもBaseID, EncryptedIDなどもある -->
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">
saml01@salesforce.com
</saml:NameID>
<!-- saml:SubjectConfirmationはSubjectについての情報 -->
<!-- 属性について Method:bearerをポイント -->
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<!-- saml:SubjectConfirmationDataはSubjectの情報 -->
<!-- 属性について NotOnOrAfter:Subjectの利用終了時刻を指定, Recipient:SPのエンドポイント, InResponseTo:ResponseメッセージのID, NotBefore:Subjectの利用の開始時刻を指定, Address:IdPのエンドポイント -->
<saml:SubjectConfirmationData NotOnOrAfter="2009-06-17T18:50:10.738Z"
Recipient="https://login.salesforce.com"/>
</saml:SubjectConfirmation>
</saml:Subject>
<!-- saml:Subjectここまで -->
<!-- saml:Conditionsここから -->
<!-- saml:Conditionsは有効性を評価するときに考慮すべき条件 -->
<!-- 属性について NotBefore:Assersionの利用の開始時刻を指定, Assersionの利用の終了時刻を指定 -->
<saml:Conditions NotBefore="2009-06-17T18:45:10.738Z"
NotOnOrAfter="2009-06-17T18:50:10.738Z">
<saml:AudienceRestriction>
<!-- SPのURL -->
<saml:Audience>https://saml.salesforce.com</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<!-- saml:Conditionsここまで -->
<!-- saml:AuthnStatementは認証情報 -->
<!-- 属性について AuthnInstant:認証した時間, SessionIndex:認証セッションのインデックス, SessionNotOnOrAfter:認証セッションの有効期限 -->
<saml:AuthnStatement AuthnInstant="2009-06-17T18:45:10.738Z">
<saml:AuthnContext>
<!-- saml:AuthnContextClassRefは認証コンテキストのクラスの参照URI -->
<saml:AuthnContextClassRef>
urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified
</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<!-- saml:AttributeStatementは属性情報 -->
<saml:AttributeStatement>
<saml:Attribute Name="portal_id">
<saml:AttributeValue xsi:type="xs:anyType">060D00000000SHZ
</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="organization_id">
<saml:AttributeValue xsi:type="xs:anyType">00DD0000000F7L5
</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="ssostartpage"
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
<saml:AttributeValue xsi:type="xs:anyType">
http://www.salesforce.com/security/saml/saml20-gen.jsp
</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="logouturl"
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<saml:AttributeValue xsi:type="xs:string">
http://www.salesforce.com/security/del_auth/SsoLogoutPage.html
</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
<!-- アサーションここまで -->
</samlp:Response>
SPは上記のような応答メッセージを受け取った後、内容を検証し、Subjectの要素の値(例ではsaml01@salesforce.com)としてSPのURLにログインします。
##Metadata
SAML連携を行うSPとIdP間で事前にお互いのことを設定しておく必要があります。
手動で設定することもできますが、設定情報を表現したXML形式のデータをSPとIdPで取り込むことで設定することができます。metadataでは、このXMLのスキーマが定義されています。
例えば、以下のような設定項目があります。
- エンティティのサポートされているSAMLバインディング
- 運用上の役割(IDP、SPなど)
- 識別子情報、サポートID属性
- 暗号化と署名のための鍵情報
実際のメタデータは、例えば以下のようになります。
(cybozu Inside Outさんに記載されていたSPのメタデータです)
接頭辞mdは、メタデータであることを示しています。
<md:EntityDescriptor entityID="https://(sub_domain).cybozu.com">
<md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<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="https://(sub_domain).cybozu.com/saml/acs" index="0"/>
</md:SPSSODescriptor>
</md:EntityDescriptor>
メタデータを理解する上で、重要なEntityDescriptorとSSODescriptor、IDPSSODescriptor、SPSSODescriptorについて見ていこうと思います。
###EntityDescriptor
メタデータ内には複数のエンティティを記述可能ですが、必ずルート要素は一つでなければなりません。エンティティが一つの場合には、EntityDescriptor要素をルート要素とし、複数の場合はEntitiesDescriptorをルート要素とします。
EntityDescriptorは次の属性と要素で構成されます。
以降、各項目で記述されている"プロファイル"は[SAMLProf]に定義されているものを指します。
- 属性
- entityID:SAMLエンティティの一意の識別子[MUST]
- ID:文書内での一意の識別子、通常は署名の三焦点として使用[OPTION]
- validUntil:メタデータの有効時間[OPTION]
- cacheDuration:メタデータをコンシューマがキャッシュする最大時間の長さ(設定が残る時間?)[OPTION]
- 要素
-
<ds:Signature>
:要素と内容を認証するためのXML署名[OPTION] -
<Extensions>
:メタデータの公開者とコンシューマで同意したメタデータの拡張[OPTION] -
<...Descriptor>
(<IDPSSODescriptor>
や<SPSSODescriptor>
、<AffiliationDescriptor>
など): 1つまた複数のロール記述子要素のシーケンス、あるいはアフィリエーション定義の特別な記述子[MUST(1または複数)] -
<Organization>
:メタデータに記述されている内容について責任がある組織(メタデータの公開者の組織)[OPTION] -
<ContactPerson>
:各種問い合わせ先[0または数個] -
<AdditionalMetadataLocation>
:他のメタデータがあれば、こちらに記述する[0または数個]
-
###SSODescriptor
SSODescriptorは後述する具体型SPSSODescriptorTypeとIDPSSODescriptorTypeの共通基本型です。SP/IdPそれぞれに必要なプロファイル情報を反映した要素になります。
SSODescriptor要素は次の属性と要素で構成されます。(親であるRoleDescriptor型も含む)
- 要素
- ID:文書内で一意の要素識別子、通常は署名の参照点として使用[OPTION]
- validUntil:要素とその内部要素の有効時間[OPTION]
- cacheDuration:メタデータをコンシューマがキャッシュする最大時間の長さ(設定が残る時間?)[OPTION]
- protocolSupportEnumeration:どのバージョンの仕様なのか(saml2.0だったら、
urn:oasis:names:tc:SAML:2.0:protocol
)[MUST] - errorURL:ロールに関係する問題解決やサポート提供にあたってユーザーを導く場所を示すオプションのURI属性[OPTION]
- 要素
-
<ds:Signature>
:要素と内容を認証するためのXML署名 -
<Extensions>
:メタデータの公開者とコンシューマで同意したメタデータの拡張[OPTION] -
<KeyDescriptor>
:エンティティがこのロールとして振る舞う際に使う暗号鍵に関する情報[0または数個] -
<Organization>
:このロールに関係付けられる組織[OPTION] -
<ContactPerson>
:このロールに関係付けられる各種問い合わせ先[OPTION] -
<ArtifactResolutionService>
:アーティファクト解決プロファイルをサポートする添字付きエンドポイント[0または数個] -
<SingleLogoutService>
:シングルログアウトプロファイルをサポートするエンドポイント[0または数個] -
<ManageNameIDService>
:名前識別子管理プロファイルをサポートするエンドポイント[0または数個] -
<NameIDFormat>
:このロールを振る舞うシステムエンティティがサポートする名前識別子フォーマット[0または数個]
-
###IDPSSODescriptor
IDPSSODescriptor要素の子要素では、IdPの固有プロファイルを記述します。
IDPSSODescriptorは次の属性と要素で構成されます。
- 属性
- WantAuthnRequestsSigned:IdPが受け取るAuthnRequestに署名が必要かどうか、省略した場合はfalse[OPTION]
- 要素
-
<SingleSignOnService>
:認証要求プロトコルをサポートするエンドポイント、ResponseLocation属性は必ず省かなければならない[1個または複数] -
<NameIDMappingService>
:名前識別子マッピングプロファイルをサポートするエンドポイント、ResponseLocation属性は必ず省かなければならない[1個または複数] -
<AssertionIDRequestService>
:アサーション要求プロトコルのためのプロファイルまたは、アサーション要求のための特別なURIバインディングをサポートするエンドポイント[0または数個] -
<AttributeProfile>
:このIdPがサポートする属性プロファイルを列挙する[0または数個] -
<saml:Attribute>
:このIdPがサポートする特定のSAML属性(サポートしている属性に関しては、アサーションに含めることが可能)[0または数個]
-
###SPSSODescriptor
SPSSODescriptor要素の子要素では、SPの固有プロファイルを記述します。
SPSSODescriptorは次の属性と要素で構成されます。
- 属性
- AuthnRequestsSigned:このSPが送るAuthnRequestに署名をするかどうか、省略した場合はfalse[OPTION]
- WantAssertionsSigned:このSPが受け取るアサーションに署名を入れることを要求するかどうか、省略した場合はfalse[OPTION]
- 要素
-
<AssertionConsumerService>
:認証要求プロトコルのプロファイルをサポートの添字付きエンドポイント[1または複数] -
<AttributeConsumingService>
:SAML属性の使用を必要とするか、使用を望むアプリケーションまたはサービス[0または数個]
-
#まとめ
いろいろと難しい言葉で書いてしまったのですが、
SPとIdP間の通信(値の受け渡し)で何か知りたいことがあれば、Bindings
SPとIdP間でやり取りする内容(メッセージ)に関してはCore
SPとIdP間で事前に共有しておく設定内容に関してはMetadata
という感じでした
#最後に
Qiitaで概要について記述されている記事は多くみたのですが、もっと細かい内容まで書いてあるものが少なかったので、自分用の備忘録として書きました。
仕様について書ききれていない部分や曖昧なところも多いので順次更新予定です。
SAMLの仕様が日本語に翻訳されたものを発見したのですが、どこが翻訳したのか見つけることができませんでした。もし分かる方いらっしゃいましたら教えていただけますと嬉しいです。
以上です。ありがとうございました!
#参考文献
[1] マイクロソフト系技術情報 Wiki「SAMLの仕様を読む」
[2]Cybozu Inside Out サイボウズのエンジニアブログ「SAML認証ができるまで」.2013
[3]Wikipedia「SAML2.0」
[4]OASIS「OASIS Security Assertion Markup Language
(SAML) V2.0 のバインディング」 OASIS標準.2005(日本語)
[5]OASIS「OASIS セキュリティ・アサーション・マークアップ
言語(SAML)メタデータ」OASIS標準.2005(日本語)
[6]OASIS「Assertions and Protocols for the OASIS
Security Assertion Markup Language
(SAML) V2.0」OASIS Standard, 2005
[7]OASIS「OASIS セキュリティアサーションマークアップ
ランゲージ(SAML)V2.0 のアサーションと
プロトコル」OASISスタンダード、2005