SmartHR Advent Calendar 2023 シリーズ2の22日目です。
IDaaSの開発を行い、100以上のSaaSとSAMLの連携を行ってきましたので、IDaaSの開発経験を踏まえてSAMLの仕組みについて記載します。
SAMLとは
SAMLは、「Security Assertion Markup Language」の頭文字を取ったもので「サムル」と読みます。
SAMLは、異なるクラウドサービス間でユーザー認証を行うためのXMLをベースにした標準規格です。
標準化団体のOASIS(Organization for the Advancement of Structured Information Standards)によって策定され、2005年に定義されたバージョン2.0(SAML2.0)が現在も主流です。
SAMLを使うことで、利用するクラウドサービスがSAMLに対応していれば、シングルサインオン(以下「SSO」といいます。)を容易に実現できます。
SAMLのメリット
SAMLには、以下のようなメリットがあります。
-
利便性の向上
SAML認証でSSOをおこなうことで、パスワード管理の必要がなくなります。
SSOを利用することで管理するIDとパスワードがひとつになるという点は、ユーザーの利便性の向上し、負担の減少につながるメリットがあります。 -
セキュリティの向上
SAML認証は、Identify Provider(以下「IdP」といいます。)という認証情報を管理するサービスがユーザーの認証情報を一元管理し、認証情報をXMLベースのSAMLアサーションとしてサービスプロバイダ(以下「SP」といいます。)に送信する事でSSOを実現します。
この仕組みにより、複数のSaaSに対してそれぞれ別のパスワードを管理する必要がなくなり、パスワード漏洩のリスクを大幅に低減することが可能になります。
また、SaaSによっては、SAML認証の設定を行う事でIDとパスワードによるログインができなくなる為、不正アクセスの防止にもなります。
SAMLの問題点
利便性の向上やセキュリティの向上等、メリットも多いSAMLですが、IDaaS側から見た問題点が以下になります。
-
SaaS側がSAML非対応の場合は対応できない
全てのSaaSがSAML認証に対応しているわけではなく、SaaS側が対応していない場合はSAML認証は行えません。 -
SAMLの利用が導入企業のコスト増加になるケースが多い
SAML対応しているSaaSでSAMLを利用する場合、有料オプションになるか、上位プランの契約が必要になるケースも多く、SAMLの利用がコスト増加になる事もあります。
SAML認証の仕組み
SAML認証の仕組みは、ユーザー、SP、IdPの3者間で成立します。
- ユーザー
- クラウドサービスの利用者のことを指します。
- SP
- Service Providerの略で認証情報を利用する側です。SaaSのことを指します。
- IdP
- Identity Providerの略で認証情報を管理・提供するサービスのことを指します。ユーザーの認証情報をSPへ渡すという役割を持っており、IDaaSはこの役割を担います。
SAMLでは主にSPとIdPの間で信頼関係を構築し、認証情報のやりとりを行います。
SAML認証のフロー
SAML認証のフローは、SPからの認証フローと、IdPからの認証フローの2種類の認証フローがあります。
SPからの認証フロー
SPが起点になる認証フローで「SP-Initiated」といいます。
このフローはユーザーがSPにログインしようとすることから始まり、以下のような順番で処理が行われます。
- SPのログイン画面を表示し、ログインボタンを押す
- SPが「SAML Request」という認証リクエストをIdPに送る
- IdPはユーザーの認証を行う為、IdPへのログインを求める ※IdPにログイン済みの場合は、5へ
- IdPへログインする
- IdPは受け取ったSAML RequestとIdPにログインしているユーザー情報を元に「SAML Response」を返却する
- 「SAML Response」をSPに送る
7. SPは受け取ったSAML Responseを検証し、問題なければSAML Responseに記載されているユーザー情報を元にログイン処理を行う
IdPからの認証フロー
IdPが起点になる認証フローで「IdP-Initiated」といいます。
このフローはユーザーがIdP画面にあるSPへログインしようとすることから始まり、以下のような順番で処理が行われます。
- IdPの画面に用意されているSPへのログインボタンを押す
- IdPはログインしているユーザー情報を元に「SAML Response」を返却する
- 「SAML Response」をSPに送る
- SPは受け取ったSAML Responseを検証し、問題なければSAML Responseに記載されているユーザー情報を元にログイン処理を行う
「SP-Initiated」「IdP-Initiated」ですが、SaaSによって対応が異なります。
「SP-Initiated」のみ対応しているSaaSもあれば、その逆で「IdP-Initiated」のみ対応している、または両方対応しているというケースもあります。
SAMLでやりとりする情報
「SP-Initiated」「IdP-Initiated」のどちらのフローも「SAML Response」を生成して、返却します。
「SAML Response」の内容について記載します。
「SAML Response」の構成は、大まかに分けると次のように分けられます。
- Response
- Responseに関する情報
- Issuer
- 発行者(IdP)に関する情報
- Singature
- 署名
- Status
- 結果のStatusCode
- Assertion
- 認証情報
以下は、Responseに署名を含めた「SAML Response」のサンプルになります。
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="pfx31b3c63c-b358-606a-eaa1-041a98dbd6d8" Version="2.0" IssueInstant="2014-07-17T01:01:48Z" Destination="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685">
<saml:Issuer>http://idp.example.com/metadata.php</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="#pfx31b3c63c-b358-606a-eaa1-041a98dbd6d8">
<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>P/Jl9zcgzgdZhTJ9F1ePzdi2CLU=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>Lz+2Icy4lgmeJqyzxhmRh6jwiBr07RXq3ePCPGOXTIzJ6h7Tl1tLDuVI9XTG5oMjKO4YcFa6e9xak7MQGT+lb3W8uuSWi8KNlz9xUal6NwIpbd2IBlSjn/rYQZh1iUDisKTFW1EXdehM0SQSr7oQ65Hu0wQGpRAk1aMJ6NZJ5T8=</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>
<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="_d71a3a8e9fcc45c9e9d248ef7049393fc8f04e5f75" Version="2.0" IssueInstant="2014-07-17T01:01:48Z">
<saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer>
<saml:Subject>
<saml:NameID SPNameQualifier="http://sp.example.com/demo1/metadata.php" 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="2024-01-18T06:21:48Z" Recipient="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="2014-07-17T01:01:18Z" NotOnOrAfter="2024-01-18T06:21:48Z">
<saml:AudienceRestriction>
<saml:Audience>http://sp.example.com/demo1/metadata.php</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2014-07-17T01:01:48Z" SessionNotOnOrAfter="2024-07-17T09:01: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">test@example.com</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>
Responseに関する情報
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="pfx31b3c63c-b358-606a-eaa1-041a98dbd6d8" Version="2.0" IssueInstant="2014-07-17T01:01:48Z" Destination="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685">
サンプル内の上記の箇所が該当します
各要素の内容は以下になります
項目 | 必須 | 内容 |
---|---|---|
ID | ○ | SAML Response側が発行する一意のID |
Version | ○ | SAMLのバージョン。基本 2.0 |
IssueInstant | ○ | SAML Responseが作成された時刻(UTC) |
Destination | △ | SAML Responseの送信先URL |
InResponseTo | △ | SAML RequestのID |
Issuerタグ | △ | Issuerに関する情報 |
Singatureタグ | △ | Singatureに関する情報 Responseに署名を含める場合はここに記載 |
Statusタグ | ○ | Statusに関する情報 |
Assertionタグ | ○ | Statusに関する情報 |
Issuerに関する情報
<saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer>
サンプル内の上記の箇所が該当します
各要素の内容は以下になります
項目 | 必須 | 内容 |
---|---|---|
Issuer | △ | SAML Responseの発行者をURL形式で設定する Responseに署名が含まれている場合は必須 |
Singatureに関する情報
<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="#pfx31b3c63c-b358-606a-eaa1-041a98dbd6d8">
<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>P/Jl9zcgzgdZhTJ9F1ePzdi2CLU=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>Lz+2Icy4lgmeJqyzxhmRh6jwiBr07RXq3ePCPGOXTIzJ6h7Tl1tLDuVI9XTG5oMjKO4YcFa6e9xak7MQGT+lb3W8uuSWi8KNlz9xUal6NwIpbd2IBlSjn/rYQZh1iUDisKTFW1EXdehM0SQSr7oQ65Hu0wQGpRAk1aMJ6NZJ5T8=</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>
サンプル内の上記の箇所が該当します
Responseに署名を含める場合はここに記載します。ここらかAssertionのどちらかに署名が必要です
Statusに関する情報
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
サンプル内の上記の箇所が該当します
各要素の内容は以下になります
項目 | 必須 | 内容 |
---|---|---|
StatusCode | ○ | 結果コード |
StatusMessage | 任意のメッセージ | |
StatusDetail | SAML Requestに対する追加情報を設定 |
StatusCodeは結果に応じて返却する値が決められています
urn:oasis:names:tc:SAML:2.0:status:{code}
のフォーマットになっており、{code}の箇所には、結果に応じて以下の値を設定します。
- Success
- 成功
- Requester
- SAML Reuestを出した側の問題で失敗
- Responder
- SAML Responseを作成した側の問題で失敗
- VersionMismatch
- SAML Reuestのバージョンが正しくない場合
成功した場合は、urn:oasis:names:tc:SAML:2.0:status:Success
となります。
また、上記以外にも設定できるStatusCodeが多数存在します。
Assertionに関する情報
<saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="_d71a3a8e9fcc45c9e9d248ef7049393fc8f04e5f75" Version="2.0" IssueInstant="2014-07-17T01:01:48Z">
<saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer>
<saml:Subject>
<saml:NameID SPNameQualifier="http://sp.example.com/demo1/metadata.php" 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="2024-01-18T06:21:48Z" Recipient="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="2014-07-17T01:01:18Z" NotOnOrAfter="2024-01-18T06:21:48Z">
<saml:AudienceRestriction>
<saml:Audience>http://sp.example.com/demo1/metadata.php</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2014-07-17T01:01:48Z" SessionNotOnOrAfter="2024-07-17T09:01: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">test@example.com</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>
サンプル内の上記の箇所が該当します
各要素の内容は以下になります
項目 | 必須 | 内容 |
---|---|---|
ID | ○ | Assertionに対する一意のID |
Version | ○ | SAMLのバージョン。基本 2.0 |
IssueInstant | ○ | Assertionが作成された時刻(UTC) |
Issuerタグ | ○ | Assertionの発行者の情報 |
Singatureタグ | △ | 署名 Assertionに署名を含める場合はここに記載 |
Subjectタグ | ○ | 認証の対象者の情報 |
Conditionsタグ | ○ | Assertionの有効性を評価する際に考慮する条件 |
AuthnStatementタグ | ○ | 認証に関する情報 |
AttributeStatementタグ | 認証の対象者の属性情報 |
[Assertion] Issuerタグ
Assertionの発行者をURL形式で設定します。
ResponseにもIssuerタグが存在しますが、Responseの発行者とAssertionの発行者が同一の場合、同じ値が設定されます。
[Assertion] Subjectタグ
認証の対象者や検証するための情報を設定します。
以下のように指定します。
<saml:Subject>
<saml:NameID SPNameQualifier="http://sp.example.com/demo1/metadata.php" 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="2024-01-18T06:21:48Z" Recipient="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"/>
</saml:SubjectConfirmation>
</saml:Subject>
項目 | 必須 | 内容 |
---|---|---|
NameIDタグ | ○ | 認証の対象者の情報 |
SubjectConfirmationタグ | ○ | 認証の対象者を検証する為の情報 |
NameIDタグには以下の項目を設定します
- SPNameQualifier
- SPの名前。SPのEntityIDが入る
- Format
- NameIDのフォーマット
- NameID
- ログインを行うユーザーを識別するデータ
SubjectConfirmationタグには以下の項目を設定します
項目 | 必須 | 内容 |
---|---|---|
Method | ○ | 検証を行う方法を指定。基本 bearerが使用される |
NotOnOrAfter | ○ | Assertionの有効期限 現在時刻がこの有効期限の時刻より後の時間の場合は無効とする |
Recipient | ○ | Assertionの検証を行うエンドポイント |
InResponseTo | △ | SAML RequestのID |
[Assertion] Conditionsタグ
Assertionに対する検証条件を設定します。
SP側ではこの条件を検証する必要があります。
以下のように条件を指定します。
<saml:Conditions NotBefore="2014-07-17T01:01:18Z" NotOnOrAfter="2024-01-18T06:21:48Z">
<saml:AudienceRestriction>
<saml:Audience>http://sp.example.com/demo1/metadata.php</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
- NotBefore
- Assertionの有効期限を設定
- 現在時刻がこの有効期限の時刻より前の時間の場合は無効とする
- NotOnOrAfter
- Assertionの有効期限を設定
- 現在時刻がこの有効期限の時刻より後の時間の場合は無効とする
- NotOnOrAfterが含まれない場合、有効期限は無期限扱いになる
- Audience
- 特定のSPに向けたAssertionである事を示す値を設定
- 事前にIdPに設定したSPの一意な値と一致する必要があります
さいごに
SAMLのXMLは巨大ですが、項目を分解してそれぞれの項目を理解すると仕様を理解しやすくなると思います。
SAMLの仕様を理解する上での参考になれば幸いです。