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がユーザーを特定できず、ログインに失敗します。連携作業でつまずきやすいのはここです。仕様で定義されているのは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 | メールアドレス形式。SaaS連携で最も使われる |
| X509SubjectName | urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName | X.509証明書のサブジェクト名 |
| WindowsDomainQualifiedName | urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName | ドメイン修飾されたWindowsアカウント名 |
| kerberos | urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos | Kerberosのプリンシパル名 |
| entity | urn:oasis:names:tc:SAML:2.0:nameid-format:entity | SAMLのエンティティ自体を指す。個人ユーザーには使わない |
| persistent | urn:oasis:names:tc:SAML:2.0:nameid-format:persistent | IdPと、SPまたはSPの連合(affiliation)の組み合わせごとに固定される仮名。実際のユーザー名などから推測できない値で生成する |
| 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が受け取るのは「認証済みの誰かである」という事実だけです。どちらを選ぶかは運用方針の話になるので、SAML統合ベストプラクティスで規模別の考え方を扱っています。
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に向けて発行するかを示します。AllowCreateにtrueを指定すると、まだ識別子が割り当てられていないユーザーに対して新規発行を認めます。IdPが要求に応えられない場合は、後述のInvalidNameIDPolicyが返ります。
IdPが対応している形式は、メタデータのNameIDFormat要素で宣言されています。連携前にここを見ておけば、SP側で選べる形式が確認できます。
Binding方式:HTTP-POSTとHTTP-Redirectの違い
SAMLのメッセージをブラウザ経由でどう運ぶかを定めたものがBindingです。Web Browser SSOで使われるのは、主にHTTP-POSTとHTTP-Redirectの2つです。
| 観点 | 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署名で担保します。
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長の制限に縛られます。2,000文字程度を目安にし、それを超えそうならHTTP-POSTに切り替えます。
この方式ではXML署名ではなく、SigAlgとSignatureのクエリパラメータでメッセージに署名します。値がアドレスバーとアクセスログに残るため、属性を含むResponseの転送には向きません。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
- W3C XML Signature
- W3C XML Encryption
関連記事
- SAML認証の実装ガイド【仕様から設定まで完全解説】
- SAML vs OAuth完全比較【プロトコル仕様から実装まで】
- Azure AD SAML連携完全手順【証明書管理までの全手順】
- Okta SSO設定完全ガイド【Sandbox環境でのテスト法】
- OneLogin SAML設定ガイド【メタデータ取得から実装まで】
- SSOセキュリティ課題完全対策【トークンハイジャック・セッション固定攻撃防止】
LOCKEDの詳細を見る
シングルサインオン・認証管理でお悩みなら、LOCKEDの無料デモをお試しください。