SAML 2.0は、XMLで認証情報を受け渡すための業界標準です。異なる組織やドメインのあいだで「このユーザーは認証済みである」という事実を、改ざんを検知できる形で伝える役目を持ちます。
ここでは実装や連携設定のときに実際に参照することになる部分を、仕様のどこに書かれているかとあわせて整理します。扱うのはAssertionの構造、NameID Formatの種類、Binding方式、署名と暗号化、そしてメタデータ交換です。
SAML 2.0はどんな規格か
SAML 2.0は2005年3月にOASISの標準仕様として承認されました。それ以前はSAML 1.1、Liberty AllianceのID-FF 1.2、Shibboleth 1.3が併存していましたが、SAML 2.0はこの3つを一本に統合したものです。現在の企業向けSSOはここを土台にしています。
認証情報を発行する側をIdP(Identity Provider)、それを受け取ってユーザーをログインさせる側をSP(Service Provider)と呼びます。IdPが署名付きのXML文書を発行し、ブラウザがそれをSPへ運び、SPが署名を検証してセッションを張る。これがSAML 2.0の骨格です。
仕様書は役割ごとに4本に分かれています。確認したい内容がどれに載っているかを知っておくと、調べ物の時間が縮みます。
| 仕様書 | 扱う内容 | 参照したい場面 |
|---|---|---|
| Core (saml-core-2.0-os) | Assertionの構造、AuthnRequest・Responseの要素、NameID Format、ステータスコード | 要素や属性の意味を確認する |
| Bindings (saml-bindings-2.0-os) | HTTP-POST・HTTP-Redirectなど転送方式 | 送信方法やエンコードを確認する |
| Profiles (saml-profiles-2.0-os) | Web Browser SSOなど利用パターンごとの必須条件 | SSOとして成立する条件を確認する |
| Metadata (saml-metadata-2.0-os) | EntityDescriptorなど設定情報の交換形式 | IdPとSPの設定交換でつまずいたとき |
IDaaS導入RFPテンプレート
IDaaS導入の社内稟議に使えるRFPテンプレート。SSO、MFA、プロビジョニングなどの要件を網羅したフォーマットです。
SAML 2.0の基本構成:Assertion・Protocol・Binding
SAML 2.0は、3つの主要コンポーネントから構成されます。
1. Assertion(アサーション)
AssertionはSAML認証の中核であり、ユーザー認証情報を表現するXML文書です。
<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
Version="2.0"
ID="_d71a3a8e9ee6e4fd0b3b1f9c0e8e5c7b"
IssueInstant="2024-03-06T10:30:00Z">
<saml:Issuer>https://idp.example.com</saml:Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<!-- XML署名 -->
</ds:Signature>
<saml:Subject>
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">
user@example.com
</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData NotOnOrAfter="2024-03-06T11:30:00Z"
Recipient="https://sp.example.com/acs"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="2024-03-06T10:25:00Z"
NotOnOrAfter="2024-03-06T11:30:00Z">
<saml:AudienceRestriction>
<saml:Audience>https://sp.example.com</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2024-03-06T10:30:00Z"
SessionIndex="s23bdf1f8e9e2e3c5d4f9a1b2c3d4e5f6">
<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="email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<saml:AttributeValue>user@example.com</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="givenName">
<saml:AttributeValue>太郎</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="sn">
<saml:AttributeValue>山田</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
Assertion内の主要要素:
必須かどうかはスキーマ上の定義ではなくProfileで決まります。以下の「扱い」は、もっとも使われるWeb Browser SSO Profileでの位置づけです。
| 要素 | 説明 | Web Browser SSOでの扱い |
|---|---|---|
| Issuer | IdPのエンティティID | 必要 |
| Subject | 認証対象ユーザーの識別子 | 必要 |
| SubjectConfirmation | Method に bearer を指定し、Recipient と NotOnOrAfter で使用範囲を限定する | 必要 |
| Conditions | AudienceRestriction で受け取るべきSPを限定し、NotBefore / NotOnOrAfter で有効期間を示す | 必要 |
| AuthnStatement | 認証時刻・方式(パスワード、多要素認証など) | 必要 |
| AttributeStatement | ユーザー属性(メールアドレス、部門など) | 任意 |
2. Protocol(プロトコル)
ProtocolはAssertion交換の手順を定義します。
AuthnRequest:SPからIdPへ送信される認証要求
<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="_8e8dc5f69a98cc4c1ff3427e5ce34606fd"
Version="2.0"
IssueInstant="2024-03-06T10:30:00Z"
Destination="https://idp.example.com/sso"
AssertionConsumerServiceURL="https://sp.example.com/acs">
<saml:Issuer>https://sp.example.com</saml:Issuer>
<samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
AllowCreate="true"/>
</samlp:AuthnRequest>
Response:IdPからSPへ返信される認証応答
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
ID="_8e8dc5f69a98cc4c1ff3427e5ce34606fd"
Version="2.0"
IssueInstant="2024-03-06T10:31:00Z"
Destination="https://sp.example.com/acs"
InResponseTo="_8e8dc5f69a98cc4c1ff3427e5ce34606fd">
<saml:Issuer>https://idp.example.com</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<saml:Assertion>
<!-- Assertion本体 -->
</saml:Assertion>
</samlp:Response>
NameID Formatの種類と使い分け
NameIDはAssertionの中でユーザーを指す識別子です。その値をどう解釈するかをFormat属性が示します。IdPが送るFormatとSP側の設定が食い違うと、認証そのものは成功しているのにSPがユーザーを特定できず、ログインに失敗します。連携作業でつまずきやすいのはここです。
Core仕様の8.3節が定義するFormatは8種類です。このURIはAssertionの<NameID>に付くほか、AuthnRequestの<NameIDPolicy>や<Issuer>のFormat属性にも同じものを使います。まず8種類を押さえておくと切り分けが早くなります。
| Format | URI | 値の意味 |
|---|---|---|
| unspecified | urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified | 解釈をIdPとSPの取り決めに委ねる |
| emailAddress | urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress | メールアドレス形式。RFC 2822のaddr-spec(local-part@domain)で、表示名・コメント・山かっこは付けない。SaaS連携で最も使われる |
| X509SubjectName | urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName | X.509証明書のサブジェクト名。XML署名仕様の<ds:X509SubjectName>と同じ書き方で、RFC 2253の規則とは異なる |
| WindowsDomainQualifiedName | urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName | DomainName\UserName の形。ドメイン名と区切りの \ は省略してもよい |
| kerberos | urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos | Kerberosのプリンシパル名。name[/instance]@REALM の形 |
| entity | urn:oasis:names:tc:SAML:2.0:nameid-format:entity | SAMLのエンティティ自体を指す。1,024文字以内のURIで、NameQualifier・SPNameQualifier・SPProvidedIDは付けない。個人ユーザーには使わない |
| persistent | urn:oasis:names:tc:SAML:2.0:nameid-format:persistent | IdPと、SPまたはSPの連合(affiliation)の組み合わせごとに固定される仮名。実際のユーザー名などから推測できない値で生成する。256文字以内 |
| transient | urn:oasis:names:tc:SAML:2.0:nameid-format:transient | そのセッションだけで有効な一時的な値。256文字以内 |
persistentとtransientの違いは、SP側でユーザーを継続的に紐づけられるかどうかです。persistentは同じユーザーなら毎回同じ値になるためSP側のアカウントと1対1で対応づけられますが、その値はSPごとに別々に発行されるので、通常は複数のSPが持ち寄って照合しても同一人物だとは分かりません。ただし仕様はSPの連合(affiliation)単位での発行も認めているため、その構成では加盟するSPが同じ識別子を共有し、連合の内側では突き合わせが成立します。名寄せを避けたいなら、ここを確認します。transientは毎回変わります。SPが受け取るのは「認証済みの誰かである」という事実だけです。なお仕様はpersistentをプライバシー保護の仕組みと位置づけていて、識別子を共有した相手以外への平文での受け渡しを禁じ、ログへの出力も相応の管理なしには認めていません。どちらを選ぶかは運用方針の話になるので、SAML統合ベストプラクティスで規模別の考え方を扱っています。
NameIDFormatが出てくる3か所
同じFormatのURIが、メタデータ・AuthnRequest・Assertionの3か所に別々の意味で現れます。どれを直せばよいかはここで決まります。
| 出てくる場所 | 書き方 | 意味 |
|---|---|---|
| IdP / SPのメタデータ | <NameIDFormat> 要素 | 自分が対応できる形式の宣言。複数並べられる |
| AuthnRequest | <NameIDPolicy> の Format 属性 | SPからIdPへの「この形式で返してほしい」という要求 |
| Assertion | <NameID> の Format 属性 | IdPが実際に発行した値の形式 |
メタデータの宣言は対応表であって、要求でも結果でもありません。SP側の管理画面で選んだ形式が応答に反映されないときは、AuthnRequestが別のFormatを要求していないか、IdPがその要求をどう解釈したかを先に見ます。
NameIDPolicyで要求する
SPからIdPへ「この形式で返してほしい」と伝えるのがAuthnRequest内のNameIDPolicy要素です。
<samlp:NameIDPolicy
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
SPNameQualifier="https://sp.example.com"
AllowCreate="true"/>
Formatに要求する形式のURIを指定し、SPNameQualifierでその識別子をどのSPに向けて発行するかを示します。IdPが要求を理解できない、あるいは受け入れられない場合は、エラーのStatusとともに後述のInvalidNameIDPolicyが返ります。
AllowCreateの既定値はfalseです。省略したままにすると、IdPがそのユーザーに識別子をまだ発行していない場合はAssertionを返せず、認証が通りません。persistentを初めて使うときにここで止まります。新規発行を認めるならtrueを明示します。
Formatを省略するかunspecifiedを指定した場合、IdPはどの種類の識別子を返してもよいことになります。狙った形式で受け取りたいなら明示します。逆にurn:oasis:names:tc:SAML:2.0:nameid-format:encryptedを指定すると、識別子は<EncryptedID>として返り、平文の<NameID>は入りません。これはNameIDPolicy専用に定義された値で、8.3節の8種類には入っていません。
IdPが対応している形式は、メタデータのNameIDFormat要素で宣言されています。連携前にここを見ておけば、SP側で選べる形式が確認できます。
IdPが受け付けるFormatは仕様より狭い
仕様が8種類を定義していても、IdPが実際に受け付けるのはその一部です。Microsoft Entra IDはAuthnRequestのNameIDPolicyについて、persistent・emailAddress・unspecified・transientの4つだけを認め、それ以外の値を指定するとエラーを返します。
返す値にも癖があります。unspecifiedを指定するとEntra IDは「形式を自分で選んでよい」と解釈し、SPごとに異なる仮名(pairwise identifier)を発行します。メールアドレスは返りません。メールアドレスで受け取る前提のSPがunspecifiedのままつながらないのは、これが原因です。AllowCreateはEntra IDでは無視されます。SPNameQualifierを指定した場合は、同じ値が応答に入ります。
切り分けの順番は、SP側の設定を疑う前に「IdPがそのFormatを受け付けるか」と「受け付けたうえで何を返すか」の2つに分けて確認することになります。IdPの公式ドキュメントで対応表を確認してから、SP側の期待値と突き合わせます。
Binding方式:HTTP-POSTとHTTP-Redirectの違い
SAMLのメッセージをどの通信方式で運ぶかを定めたものがBindingです。Bindings仕様は6種類を定義していますが、ブラウザ経由のSSOで実際に設定するのはHTTP-POSTとHTTP-Redirectの2つです。
仕様が定めるBindingは6種類
| Binding | URN | 運び方と主な用途 |
|---|---|---|
| SOAP | urn:oasis:names:tc:SAML:2.0:bindings:SOAP | SOAP over HTTPでSPとIdPが直接やり取りする。Artifactの引き換えや属性の問い合わせ |
| Reverse SOAP (PAOS) | urn:oasis:names:tc:SAML:2.0:bindings:PAOS | ブラウザ以外のクライアントが仲介する。後述のECP Profileで使う |
| HTTP-Redirect | urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect | GETのクエリパラメータで運ぶ。AuthnRequestの送信 |
| HTTP-POST | urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST | formのhidden fieldに入れてPOSTする。Responseの送信 |
| HTTP-Artifact | urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact | 本体ではなく引換券(artifact)だけをブラウザ経由で渡し、本体はSOAPで直接取りに行く |
| URI | urn:oasis:names:tc:SAML:2.0:bindings:URI | URIを指定してAssertionを1件取得する |
Web Browser SSO ProfileはAuthnRequestにHTTP-Redirect・HTTP-POST・HTTP-Artifactのいずれかを認め、Responseには HTTP-POSTかHTTP-Artifactのみ を認めています。ResponseにHTTP-Redirectを使うことは仕様が明確に禁じていて、理由はResponseがブラウザの許すURL長をたいてい超えるからです。
| 観点 | HTTP-POST | HTTP-Redirect |
|---|---|---|
| 運び方 | formのhidden fieldに入れてPOST | URLのクエリパラメータに載せてGET |
| エンコード | base64 | DEFLATE圧縮 → base64 → URLエンコード |
| サイズの制約 | 仕様上の上限はない | ブラウザとサーバーのURL長の制限を受ける |
| 署名 | XML署名をメッセージ内に含める | クエリパラメータ SigAlg と Signature で付ける |
| 主な用途 | Response(Assertionの送信) | AuthnRequest |
HTTP-POST Binding(Responseの標準的な転送方式)
SPのACS(Assertion Consumer Service)URLへ、POSTリクエストでAssertionを送信します。
<form method="post" action="https://sp.example.com/acs">
<input type="hidden" name="SAMLResponse"
value="PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDOYWY6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24i..."/>
<input type="hidden" name="RelayState" value="https://example.com/page"/>
<input type="submit" value="Continue"/>
</form>
Assertionはbase64にしてPOST本文で送るため、URL長の制限を受けません。属性が多くても、証明書を含んでいても運べます。Responseの転送にこの方式を使うのはそのためです。通信内容はTLSで保護し、Assertion自体の真正性はXML署名で担保します。
hidden fieldの名前は、応答ならSAMLResponse、要求ならSAMLRequestと決まっています。送出するHTMLはXHTMLに準拠させます。
RelayStateには仕様上の長さ制限があります。80バイトを超えてはいけないので、長い遷移先URLをそのまま入れると収まりません。鍵だけを載せてURL本体はサーバー側で保持するのが実装の定石です。値はブラウザを経由する途中で書き換えられるため、チェックサムや推測できない値を使って改ざんを検知できるようにしておきます。
HTTP-Redirect Binding
IdPのSSOエンドポイントへ、GETリクエストでAuthnRequestを送信します。
https://idp.example.com/sso?SAMLRequest=jVDPb%2BMEcwx4jFKr%2Fn5YxIwOAAA&RelayState=somevalue
&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256
&Signature=NyZ3S8T1U2V3W4X5Y6Z7A8B9C0D1E2F3G4H5I6J7K8L9M0N1O2P3Q4R5S6T7U8%3D
メッセージはDEFLATEで圧縮してからbase64とURLエンコードを施し、SAMLRequestまたはSAMLResponseというクエリパラメータに載せます。圧縮するのはURLに収めるためで、それでも上限はブラウザやサーバーのURL長の制限に縛られます。仕様はこの上限を数値で定めていないので、長くなりそうならHTTP-POSTに切り替えます。
この方式ではXML署名を使いません。プロトコルメッセージ自体に付いていた<ds:Signature>要素を取り除いてから圧縮し、代わりにSigAlgとSignatureのクエリパラメータで署名します。署名の対象は、URLエンコード済みのパラメータをこの順番で連結した文字列です。
SAMLRequest=value&RelayState=value&SigAlg=value
SAMLResponse=value&RelayState=value&SigAlg=value
RelayStateが無いときは、RelayState=をパラメータごと署名の対象から外します。空の値で入れてはいけません。固定の書式だと思って常に入れると、受け取る側が組み立てるバイト列と合わず、署名が通りません。
URL上の並び順は仕様が決めていないので、検証する側がこの順番に並べ替えてから計算します。URLエンコードの仕方も一通りではないため、検証にはクエリで受け取った文字列をそのまま使い、値を取り出してから再エンコードしません。クエリに他のパラメータが付いていても、署名の対象には入りません。HTTP-Redirectで署名検証が落ちる原因の多くはこのあたりです。
署名済みのAssertionのような内容を含むメッセージは、この方式で運ばないことになっています。圧縮してもURLに収まらないからです。HTTP-RedirectはAuthnRequestの送信に使い、値がアドレスバーとアクセスログに残る前提で扱います。
XML署名とXML暗号化の実装仕様
XML署名(W3C xmldsig)
AssertionおよびResponseに対して、デジタル署名を施すことで、改ざん検知と発信者認証を実現します。
<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/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference URI="#_d71a3a8e9ee6e4fd0b3b1f9c0e8e5c7b">
<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/2001/04/xmlenc#sha256"/>
<ds:DigestValue>jFDPb/...=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
5Xda3xD3M7EzM9pL2Q4R9S8T1U2V3W4X5Y6Z7A8B9C0D1E2F3G4H5I6J7K8L9M0N1O2P3Q4R5S6T7U8=
</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>
MIIDXTCCAkWgAwIBAgIJAKHHCgVDRV2dMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNVBAYTAlB...
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
署名検証フロー(SPでの処理):
- AssertionのIssuerを確認
- IdPのメタデータからIdPの公開鍵を取得
- SignatureValueを公開鍵で検証
- DigestValueがAssertion内容と一致することを確認
- 署名が有効な場合のみ、Assertionを受け入れ
XML暗号化
機密属性情報(パスワード、マイナンバーなど)が含まれる場合、XML暗号化によってAssertionを保護します。
<saml:Assertion>
<!-- 平文の要素 -->
<xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"
Type="http://www.w3.org/2001/04/xmlenc#Element">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<xenc:EncryptedKey>
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"/>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>
<!-- SP公開鍵 -->
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
<xenc:CipherData>
<!-- 暗号化されたセッション鍵 -->
</xenc:CipherData>
</xenc:EncryptedKey>
</ds:KeyInfo>
<xenc:CipherData>
<!-- 暗号化されたAttribute -->
</xenc:CipherData>
</xenc:EncryptedData>
</saml:Assertion>
SAML Responseの検証とステータスコード
連携がうまくいかないとき、最初に見るのはResponse内のStatusCodeです。Success以外の値は、SPとIdPのどちらを調べればよいかを教えてくれます。トップレベルのStatusCodeは4種類しかありません。
| Value | 意味 |
|---|---|
urn:oasis:names:tc:SAML:2.0:status:Success | 要求は正常に処理された |
urn:oasis:names:tc:SAML:2.0:status:Requester | 要求した側(SP)に原因がある |
urn:oasis:names:tc:SAML:2.0:status:Responder | 応答する側(IdP)で処理できなかった |
urn:oasis:names:tc:SAML:2.0:status:VersionMismatch | SAMLのバージョンが合わない |
Requesterが返ったらAuthnRequestの内容とSP側の設定を、ResponderならIdPの認証処理と設定を疑います。原因をもう一段細かく伝える場合は、2階層目のStatusCodeが入れ子で付きます。AuthnFailedならIdPはユーザーを認証できず、InvalidNameIDPolicyなら要求されたNameID Formatに応えられず、RequestDeniedなら要求自体を拒否したという意味になります。IdPは運用上の配慮から詳細を省くこともあるため、2階層目が無いケースもあります。
StatusCodeがSuccessなのにログインできない場合は、SP側の検証のどこで落ちているかを次の順で確認します。大半の失敗はこの5点のどれかに当たります。
- 署名が検証できているか(前述の検証フロー)
NotBeforeとNotOnOrAfterの有効期間内か- AudienceRestrictionに自分のエンティティIDが入っているか
InResponseToが送信したAuthnRequestのIDと一致しているか- NameIDのFormatと値がSP側の設定と合っているか
Profile:使用パターン定義
Web Browser SSO Profile
最も一般的なSAML利用シーン:ブラウザでログインが必要な場合
User Agent (Browser)
↓
1. ユーザーがSPのログインページにアクセス
↓
2. SPがAuthnRequestを生成、ブラウザを経由してIdPへ送信
↓
3. ユーザーがIdPで認証(パスワード、MFA等)
↓
4. IdPがSAML ResponseをACSにPOST
↓
5. ブラウザがFormをAutoSubmit
↓
6. SPがResponseを検証、セッション発行
↓
7. ユーザーが保護されたリソースにアクセス可能
Enhanced Client or Proxy Profile
OutlookなどのリッチクライアントでのSSO実装
メタデータ交換
IdPメタデータ
IdPが公開する、SPが信頼するための情報源
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
entityID="https://idp.example.com">
<IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<KeyDescriptor use="signing">
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Data>
<X509Certificate>
MIIDXTCCAkWgAwIBAgIJAKHHCgVDRV2dMA0GCSqGSIb3...
</X509Certificate>
</X509Data>
</KeyInfo>
</KeyDescriptor>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="https://idp.example.com/sso"/>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="https://idp.example.com/sso"/>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>
</IDPSSODescriptor>
</EntityDescriptor>
SPメタデータ
SPが公開する、IdPが信頼するための情報源
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
entityID="https://sp.example.com">
<SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<KeyDescriptor use="encryption">
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Data>
<X509Certificate>
MIIDXTCCAkWgAwIBAgIJAKHHCgVDRV2dMA0GCSqGSIb3...
</X509Certificate>
</X509Data>
</KeyInfo>
</KeyDescriptor>
<AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="https://sp.example.com/acs" isDefault="true" index="0"/>
<AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="https://sp.example.com/acs-redirect" index="1"/>
</SPSSODescriptor>
</EntityDescriptor>
LOCKED MSO でのSAML設定項目
LOCKED MSO はSAML認証に対応しており、SAMLに対応したクラウドサービスとSSOで連携できます。設定時に決めることになる項目と、実務でよく採る値は次のとおりです。
【認証方式】
- プロトコル: SAML 2.0
- NameID Format: emailAddress(SaaS連携で一般的)
- Binding: HTTP-POST
- 署名アルゴリズム: SHA-256
- Assertionの暗号化: AES-256
【属性マッピング】
IdPが送る属性 → サービス側の項目
- mail → メールアドレス
- givenName → 名
- sn → 姓
- department → 部門
【有効期間】
- Assertionの有効期限: 5分程度
- セッションの有効期限: 業務時間に合わせる
実装上の注意点
証明書の更新管理
SAML署名用証明書は1〜3年程度の有効期限で作られることが多く、期限切れに気づかないまま当日を迎えると、SSO経由のログインが一斉に止まります。期限は監視の対象として持ちます。切り替え期間中は、新旧どちらの証明書でも署名検証が通るようにしておくと安全です。メタデータをURLで公開しておくと差し替えを伝えやすくなりますが、自動で反映されるかはSP側の実装次第です。SPが定期的にメタデータを再取得する機能を持ち、その設定が有効になっていなければ、初回にインポートした内容のまま固定され、古い証明書を使い続けることになります。切り替えの前に、そこを確認します。
時刻同期(ClockSkew)
Assertionの有効性は、IssueInstant と NotBefore / NotOnOrAfter の時刻で判定します。IdPとSPのサーバー時刻がずれていると、正しく発行されたAssertionでも期限切れや発行前と見なされて認証が失敗します。双方でNTPによる時刻同期を前提にしたうえで、許容するズレを数十秒の幅で設定しておきます。
まとめ
SAML 2.0は要素の数が多く複雑に見えますが、Assertionの中身、それを運ぶBinding、成否を伝えるStatusCodeの3つに分けて見れば、設定のどこでつまずいているかは切り分けられます。連携作業で実際に詰まるのはNameID Formatの不一致と時刻ズレ、それに証明書の期限切れで、いずれも事前に確認できる項目です。LOCKED MSO は、この複雑さを管理画面の設定項目に落とし込んだ形でSAML SSOを提供しています。
参考資料・外部リンク
- OASIS SAML 2.0 Core Specification(Assertion・NameID Format・ステータスコード)
- OASIS SAML 2.0 Bindings(HTTP-POST / HTTP-Redirect)
- OASIS SAML 2.0 Profiles(Web Browser SSO Profile)
- OASIS SAML 2.0 Metadata(EntityDescriptor)
- OASIS SAML 2.0 Technical Overview
- シングル サインオン SAML プロトコル(Microsoft Entra ID が受け付ける NameIDPolicy の Format)
- W3C XML Signature
- W3C XML Encryption
関連記事
- SAML認証の実装ガイド【仕様から設定まで完全解説】
- SAML vs OAuth完全比較【プロトコル仕様から実装まで】
- Azure AD SAML連携完全手順【証明書管理までの全手順】
- Okta SSO設定完全ガイド【Sandbox環境でのテスト法】
- OneLogin SAML設定ガイド【メタデータ取得から実装まで】
- SSOセキュリティ課題完全対策【トークンハイジャック・セッション固定攻撃防止】
LOCKEDの詳細を見る
シングルサインオン・認証管理でお悩みなら、LOCKEDの無料デモをお試しください。