SAML 2.0のIdPは、利用者を認証し、署名付きのアサーションをSP(業務アプリやSaaS)へ渡す側のシステムです。この記事では、IdPがSPに約束する項目をOASISのメタデータ仕様で確かめたうえで、IDaaSを契約するか、KeycloakやSimpleSAMLphpで自前のSAML IdPを建てるかを比べます。Keycloak 26.8系での起動とSP登録、Name ID形式と署名の設定、グループ属性のマッピング、SPへ渡す前にIdPメタデータを検査するPythonコード、署名鍵の切り替えまで、IdPを運用する側の手順をまとめます。
まとめ:SAML 2.0 IdPは社員SSOならIDaaS、自前は条件付きでKeycloak
社員が数十本のSaaSへシングルサインオンするだけなら、SAML 2.0 IdPは自前で建てずにIDaaSを契約します。IdPの本体は数コマンドで起動できますが、本番で手間がかかるのは起動後です。署名鍵の定期的な切り替え、停止させない冗長構成、パッチ版への追従が、IdPを持つ限り続きます。
自前で建てる場合はKeycloakを第一候補にします。SP登録、Name ID形式、署名、属性マッパーを管理画面で設定でき、SPのメタデータの取り込みにも対応しています。SimpleSAMLphpはPHPの設定ファイルで動く軽量な実装で、既存のPHP環境に小さく足したいときの選択肢です。
どちらを選んでも、SPへメタデータを渡す前にentityID、SSOのURL、署名用証明書を機械的に確かめる工程を入れてください。SP登録でつまずく原因の多くは、この3つの食い違いです。
SAML 2.0 IdPがSPに渡すものとメタデータで約束する項目
SAMLの全体像と認証フローはSAMLとは?認証フロー・IdP/SPの仕組みとOAuth・OIDCとの使い分けを実装視点で解説で扱っています。ここではIdPを建てる側が知っておく範囲に絞ります。
IdPの仕事は利用者の認証とアサーション発行の2つに集約される
SAML 2.0は2005年3月にOASIS標準になった仕様で、SAML 2.0 Core仕様がアサーションとプロトコルのXMLを定めています。IdPが受け持つのは、利用者が誰かを確かめることと、その結果をアサーションに書いて署名し、SPへ返すことの2つです。
パスワードの照合、多要素認証、社内ディレクトリとの連携は、すべてIdPの内側で完結します。SPは署名を検証し、アサーションに書かれた識別子と属性を信じてログインさせるだけです。したがってIdPの設計で決めるべきことは、認証の強さと、SPごとに何をアサーションに入れて渡すかの2点に絞られます。SP側の検証順序はSAMLアサーションとは:XML構造の読み方と署名・Audience・有効期限の検証実装で解説しました。
IDPSSODescriptorに並ぶ署名要求・SSOのURL・Name ID形式
IdPとSPの約束は、メタデータと呼ばれるXMLで交換します。SAML 2.0 Metadata仕様の2.4.3節は、IdPの能力を表す要素としてIDPSSODescriptorを定めています。SPへ渡す前に見ておく項目は次の4つです。
WantAuthnRequestsSigned:IdPがSPからの認証要求に署名を求めるか。省略時はfalseSingleSignOnService:認証要求を受けるURLとバインディング。1つ以上が必須NameIDFormat:IdPが発行できる利用者識別子の形式KeyDescriptor:アサーションの署名に使う証明書
このうち最もよく食い違うのは証明書です。IdP側で鍵を切り替えたのにSPが古い証明書を持ち続けると、署名検証で全員がログインできなくなります。メタデータ全体の読み方とSP側の更新手順はSAMLメタデータとは:EntityDescriptorの読み方と署名証明書の更新手順にまとめています。
IdPの調達をIDaaS・Keycloak・SimpleSAMLphpで比べる観点
SAML 2.0 IdPを用意する方法は、IDaaSの契約か、OSSのIdPを自社で運用するかに分かれます。OSSは今回、実装例の多いKeycloakとSimpleSAMLphpを取り上げます。
IDaaS・Keycloak・SimpleSAMLphpを運用の負担と拡張性で比べた表
| 観点 | IDaaS(Entra ID・Okta等) | Keycloak | SimpleSAMLphp |
|---|---|---|---|
| 2026年11月時点の版 | 提供側が随時更新 | 26.8.1(2026-10-13) | 2.5.3.1(2026-08-09) |
| 実装言語・動作環境 | クラウドの提供サービス | Java・コンテナ | PHP・Webサーバー |
| SP登録の方法 | 管理画面・SaaSカタログ | 管理画面・メタデータ取り込み | PHPの配列で記述 |
| 対応プロトコル | SAML・OIDC | SAML・OIDC | SAMLが主 |
| 鍵更新・冗長化の担当 | 提供側 | 自社 | 自社 |
| 向く規模 | 社員SSO全般 | 数十〜数百のSP | 少数のSP |
表の版番号と日付は、Keycloak 26.8.1のリリースとSimpleSAMLphp v2.5.3.1のリリースで確かめた値です。IDaaSの細かな違いはIDaaSとは?SSO・IdPとの違いとSAML・OIDC・SCIM連携を実装視点で解説で比べています。
自前のIdPが本番運用で抱える鍵更新・冗長化・パッチ追従の負担
OSSのIdPは無償でも、運用の工数は無償になりません。IdPが止まると、つながっている全SPへのログインが同時に止まります。単一障害点になる前提で、2台以上の構成とセッションの共有を設計する必要があります。
パッチ版の頻度も見積もりに入れます。Keycloakは26.8.0が2026年10月1日、26.8.1が10月13日に公開されており、2週間ほどで次の版が出ました。認証基盤の脆弱性は全SPに波及するため、検証環境で確かめてから本番へ上げる手順を、月に1回以上回せる体制が前提です。
この体制を社内で組めないなら、OSSのIdPは選ばないでください。
Keycloak 26.8系でSAML 2.0 IdPを起動しSPを登録する作業手順
ここからはKeycloakをIdPとして使う最小の手順です。Keycloak自体の導入やDBの設定はKeycloakのデータベース設定と使い方|PostgreSQL接続・対応DB・バックアップから権限モデルまでを参照してください。
docker runで検証用Keycloakを起動しIdPメタデータを取得する
KeycloakのGetting started(Docker)に載っている起動コマンドです。start-devは開発用のモードで、本番では使いません。
docker run -p 127.0.0.1:8080:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:26.8.1 start-dev
# 管理画面(http://localhost:8080/admin)でrealm「corp」を作成したあと、
# SPへ渡すIdPメタデータを取得する
curl -s http://localhost:8080/realms/corp/protocol/saml/descriptor -o idp-metadata.xml
IdPメタデータのURLは、KeycloakのServer Administration Guideにroot/realms/{realm-name}/protocol/saml/descriptorの形で書かれています。SPの担当者にはこのURLを伝えるか、取得したXMLを渡します。
SPのメタデータを取り込みClient IDと受信URLをそろえるSP登録
Keycloakでは、SPを「Client type=SAML」のクライアントとして登録します。手で入力するより、SPが公開するメタデータのXMLを「Import client」で読み込むほうが入力の誤りを減らせます。手入力する場合に外せない項目は次のとおりです。
- Client ID:SPのentityIDと完全に一致させる。Keycloakは認証要求のIssuerとこの値を照合してクライアントを選ぶ
- Valid Redirect URIs:SPのACSのURLを登録する。ワイルドカードはURLの末尾だけに使える
- Master SAML Processing URL:ACSとシングルログアウトに共通で使うURL。登録したValid Redirect URIsのパターンに合う必要がある
- IDP-Initiated SSO URL name:IdP起点のログインを使わないなら空欄のままにする
Client IDの1文字違い、たとえば末尾のスラッシュの有無だけで、Keycloakは該当クライアントを見つけられずエラーを返します。IdP起点を空欄にしておく理由はSAML IdP-initiatedとは:SP-initiatedとの違いとACS実装の判断で説明しました。
Name ID形式と署名の設定をSPの要求に合わせるときの判断基準
Name ID Formatは、認証要求にNameIDPolicyが無いときに使う既定の形式です。SPがメールアドレスでの照合を求めるならemailを選び、要求の指定を無視させたい場合だけForce Name ID FormatをONにします。形式が合わないときの症状と切り分けはSAML NameIDFormatとメタデータの不一致でSSOが失敗する原因の切り分けが詳しいです。
署名は、応答全体を署名するSign Documentsと、アサーションを署名するSign Assertionsを分けて設定できます。SPのライブラリがアサーションの署名だけを検証する実装なら、Sign AssertionsをONにします。迷ったら両方ONで構いません。Signature AlgorithmはSHA-256系を選びます。公式ガイドはSHA1系を非推奨とし、Java 17以上で動くSPではSHA1の署名検証が通らないと明記しています。
属性マッピングでメール・氏名・グループをSPの属性名へ合わせる
アサーションに入れる属性を決めるのは、クライアントのマッパーです。KeycloakのSAML用マッパーは、Keycloak標準の項目(メールや氏名)を出す「User Property」、独自に追加したユーザー属性を出す「User Attribute」、所属グループを出す「Group list」の3種類を押さえれば足ります。属性名は、SPが期待する名前と大文字小文字まで一致させます。逆にKeycloakを中継役に置き、Entra IDを外部IdPとして登録する手順はKeycloak SAML設定:Entra IDを外部IdPにする手順と属性マッパーで解説しています。
つまずきやすいのはグループです。Group listマッパーのソース(26.8.1)では、設定項目「Full group path」の既定値がtrueで、グループ名が/top/level1/level2のようなパスで送られます。SP側の権限設定が「sales」のような名前だけを待っているなら、この項目をOFFにしてください。
SimpleSAMLphp 2.5系で軽量なIdPを建てる鍵生成とSP登録
PHPのWebサーバーが既にあり、つなぐSPが数本だけなら、SimpleSAMLphpで足りる場合があります。手順はSimpleSAMLphp Identity Provider QuickStartに沿っています。
opensslの鍵生成とsaml20-idp-hosted.phpに書く最小の設定
まずconfig/config.phpで'enable.saml20-idp' => trueを設定してIdP機能を有効にします。続けて署名用の鍵と自己署名証明書を作り、cert/ディレクトリに置きます。
openssl req -newkey rsa:3072 -new -x509 -days 3652 -nodes \
-out example.org.crt -keyout example.org.pem
QuickStartの例は有効期間3652日(約10年)です。SimpleSAMLphpはRSAの証明書だけに対応し、DSAは使えません。IdP本体の設定はmetadata/saml20-idp-hosted.phpに書き、entityID、この鍵と証明書、認証に使う認証ソース(config/authsources.phpで定義)の3点を指定します。社内のLDAPや既存の会員DBを認証源にする場合は、同梱のldap:LDAPやsqlauth:SQLモジュールを認証ソースに選びます。
saml20-sp-remote.phpへSPを追加しIdPメタデータを渡す手順
SPの情報はmetadata/saml20-sp-remote.phpに配列で追加します。QuickStartの最小例を、架空のSPのURLに置き換えたものです。
<?php
$metadata['https://sp.example.co.jp/saml/metadata'] = [
'AssertionConsumerService' => [
[
'Location' => 'https://sp.example.co.jp/saml/acs',
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
],
],
'SingleLogoutService' => [
[
'Location' => 'https://sp.example.co.jp/saml/logout',
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
],
],
];
配列のキーがSPのentityIDです。SPがXMLのメタデータしか出せない場合は、管理画面の/module.php/admin/federation/metadata-converterで配列への変換が可能です。SPへ渡すIdPメタデータは/saml2/idp/metadata.phpで公開されます。同じマシンでIdPとSPを試す場合は、Cookieの干渉を避けるためホスト名を分けるよう、QuickStartに注意書きがあります。
SPへ渡す前のIdPメタデータ検査コードと署名鍵の切り替え手順
KeycloakでもSimpleSAMLphpでも、SPの担当者へ渡す前にメタデータを一度機械的に確かめておくと、登録後の行き違いが減ります。
標準ライブラリでentityID・SSOのURL・署名証明書を確かめるコード
次のidp_meta_check.pyは、外部パッケージなしで動きます。Metadata仕様の名前空間でIDPSSODescriptorを読み、httpのまま残ったURLや、署名用証明書の枚数を報告します。
import base64, hashlib, sys
import xml.etree.ElementTree as ET
NS = {
"md": "urn:oasis:names:tc:SAML:2.0:metadata",
"ds": "http://www.w3.org/2000/09/xmldsig#",
}
BIND = "urn:oasis:names:tc:SAML:2.0:bindings:"
def check(path):
"""IdPメタデータをSPへ渡す前に、登録で詰まりやすい項目を確かめる"""
root = ET.parse(path).getroot()
idp = root.find("md:IDPSSODescriptor", NS)
if idp is None:
sys.exit("NG: IDPSSODescriptor がない(SPのメタデータを渡していないか確認)")
entity_id = root.get("entityID", "")
print("entityID :", entity_id)
print("WantAuthnRequestsSigned:", idp.get("WantAuthnRequestsSigned", "false (省略)"))
print("validUntil/cacheDuration:", root.get("validUntil"), "/", root.get("cacheDuration"))
warn = []
if not entity_id.startswith("https://"):
warn.append("entityID が https で始まらない(検証環境の値のまま渡していないか)")
sso = {e.get("Binding").replace(BIND, ""): e.get("Location")
for e in idp.findall("md:SingleSignOnService", NS)}
for b, loc in sso.items():
print(f"SSO {b:<13}: {loc}")
if not loc.startswith("https://"):
warn.append(f"SSO {b} の Location が https でない")
if "HTTP-Redirect" not in sso and "HTTP-POST" not in sso:
warn.append("ブラウザ向けの HTTP-Redirect と HTTP-POST の SSO がどちらもない")
for f in idp.findall("md:NameIDFormat", NS):
print("NameIDFormat :", f.text.rsplit(":", 1)[-1])
certs = []
for kd in idp.findall("md:KeyDescriptor", NS):
if kd.get("use") in (None, "signing"):
for c in kd.findall(".//ds:X509Certificate", NS):
der = base64.b64decode("".join(c.text.split()))
certs.append(hashlib.sha256(der).hexdigest()[:16])
print("署名用証明書の数 :", len(certs), certs)
if not certs:
warn.append("署名用の KeyDescriptor がない")
elif len(certs) >= 2:
warn.append("署名用証明書が2枚ある=鍵の切り替え中。SPが両方を信頼しているか確認")
print("--")
for w in warn:
print("WARN:", w)
print("OK" if not warn else f"{len(warn)} 件の確認事項")
if __name__ == "__main__":
check(sys.argv[1])
鍵の切り替え中を想定し、署名用証明書を2枚載せたメタデータで試します。証明書の中身はダミー値です。
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
entityID="https://sso.example.co.jp/realms/corp">
<md:IDPSSODescriptor WantAuthnRequestsSigned="true"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:KeyDescriptor use="signing"><ds:KeyInfo><ds:X509Data>
<ds:X509Certificate>RFVNTVktQ0VSVC0yMDI2LTA1</ds:X509Certificate>
</ds:X509Data></ds:KeyInfo></md:KeyDescriptor>
<md:KeyDescriptor use="signing"><ds:KeyInfo><ds:X509Data>
<ds:X509Certificate>RFVNTVktQ0VSVC0yMDI2LTEx</ds:X509Certificate>
</ds:X509Data></ds:KeyInfo></md:KeyDescriptor>
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="https://sso.example.co.jp/realms/corp/protocol/saml"/>
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat>
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="https://sso.example.co.jp/realms/corp/protocol/saml"/>
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="https://sso.example.co.jp/realms/corp/protocol/saml"/>
</md:IDPSSODescriptor>
</md:EntityDescriptor>
Python 3.14.10でpython idp_meta_check.py idp-metadata.xmlを実行した結果です。
entityID : https://sso.example.co.jp/realms/corp
WantAuthnRequestsSigned: true
validUntil/cacheDuration: None / None
SSO HTTP-POST : https://sso.example.co.jp/realms/corp/protocol/saml
SSO HTTP-Redirect: https://sso.example.co.jp/realms/corp/protocol/saml
NameIDFormat : persistent
NameIDFormat : emailAddress
署名用証明書の数 : 2 ['4bac4c91cad7f6cc', '682bc2b577a569c3']
--
WARN: 署名用証明書が2枚ある=鍵の切り替え中。SPが両方を信頼しているか確認
1 件の確認事項
前の章のstart-devで取得したメタデータを通すと、URLがhttpのため、entityIDとSSOのLocationに警告が出ます。本番のホスト名とTLSを設定してから取り直したメタデータを渡す、という確認に使えます。
署名鍵を止めずに切り替えるKeycloakの能動鍵と受動鍵の運用
Server Administration Guideの「Configuring realm keys」によると、Keycloakのrealmは署名に使う能動の鍵ペアを常に1つだけ持ち、過去の署名を検証するための受動の鍵を複数持てます。新しい鍵を既存より高い優先度で追加すると、以後の署名に使われるのは新しい鍵です。ガイドは3〜6か月ごとの鍵の作成と、作成から1〜2か月後の古い鍵の削除を目安に挙げています。
SAMLで問題になるのは、SP側が証明書を手で貼り付けて固定している場合です。この場合、新しい鍵へ切り替えた瞬間にSPの署名検証が失敗します。手順は次の順にしてください。
- 新しい鍵を、既存より低い優先度で追加する(メタデータに2枚載る)
- 上の検査コードで2枚載っていることを確かめ、全SPに新しい証明書の登録を依頼する
- 全SPの登録を確認してから、新しい鍵の優先度を上げて能動にする
- 1〜2か月おいて古い鍵を受動から無効、削除の順に外す
SP数が多いほど、手順2の連絡と確認に時間がかかります。メタデータのURLから証明書を自動で取り直せるSPを優先して接続しておくと、この作業を減らせます。
SAML 2.0 IdPを自前で建てる条件とIDaaSに任せる業務の場面
受託開発で認証基盤の相談を受けたときに出している結論を、条件付きで書きます。
OSSのIdPを選ぶのは閉域網・独自の認証源・自社SaaSが顧客のIdPになる時
Keycloakなどを自前で建てるのは、次のいずれかに当たる場合に限ります。
- 工場や医療機関の閉域網で、クラウドのIDaaSに到達できない
- 既存の会員DBや独自の認証要素を認証源に使い続けたい
- 自社が提供するSaaSや会員基盤が、取引先のSPに対してIdPとして振る舞う
- つなぐSPや利用者の数が多く、IDaaSの利用者単位の費用が合わない
どれにも当たらず、社員が既製のSaaSへ入るためのSSOが目的なら、IDaaSを選びます。前の章の鍵の切り替えや冗長化を自社で抱えても、利用者から見た機能は増えないためです。IDaaSがEntra IDで社員のIDがActive Directoryにある構成は、SAML ADの構成手順:AD FSとEntra IDでSSOを張る設定と移行判断で扱っています。IdPとSPの信頼関係を複数の組織で組む設計はフェデレーション認証とは?IdPとSPの信頼設計と属性連携を実装視点で解説で扱っています。
IdP構築を見送るべき失敗パターンと自社SaaS側で先に決めること
見送るべき典型は、IdPを1台のサーバーに建て、鍵の切り替えとパッチ適用の担当者を決めないまま本番に出すケースです。証明書の期限切れやIdPの停止で全SPに同時にログインできなくなり、復旧に必要な管理者もSSOで入れない、という事態になります。管理者用のローカルアカウントと、IdPを介さない緊急の入口を残さない設計は差し戻します。
自社SaaSにSAMLログインを足したい場合は、IdPを建てる前に、SPとして取引先ごとのIdPを受け入れる設計が先です。その手順はSAML連携の実装手順:自社SaaSにテナント別IdP設定とJITを組み込むで解説しました。IDaaSと自前IdPの比較から、Keycloakの冗長構成や既存DBとの連携まで含めた構築は、一創の認証基盤・ID管理システム開発で設計から運用まで対応しています。
よくある質問
SAML 2.0 IdPの構築と選定について、よく出る質問をまとめました。
SAML 2.0のIdPとSPの違いは何ですか?
IdPは利用者を認証し、結果をアサーションに書いて署名する側です。SPはそのアサーションを受け取り、署名を検証してログインさせる側で、業務システムやSaaSが当たります。パスワードや多要素認証の情報を持つのはIdPだけで、SPは利用者のパスワードを扱いません。メタデータでは、IdPはIDPSSODescriptor、SPはSPSSODescriptorで能力を表します。
無料で使えるSAML 2.0 IdPはありますか?
OSSのKeycloakとSimpleSAMLphpは無償で使えます。どちらも2026年11月時点で更新が続いており、Keycloakは26.8.1、SimpleSAMLphpは2.5.3.1が出ています。ただしサーバー費用と、鍵の切り替え、冗長化、パッチ適用の工数は自社の負担です。社員のSSOが目的なら、この工数とIDaaSの利用料を比べて決めてください。
KeycloakをSAML IdPにするときSPのClient IDには何を入れますか?
入力する値は、SPのentityIDそのものです。Keycloakは認証要求のIssuerとClient IDを照合してクライアントを選ぶため、末尾のスラッシュの有無まで一致している必要があります。SPがメタデータのXMLを出せるなら、Import clientで読み込むと、entityIDとACSのURLが自動で入り、入力の誤りを防げます。
IdPの署名証明書の有効期限はどれくらいにしますか?
SimpleSAMLphpのQuickStartは3652日(約10年)の自己署名証明書を例にしています。SAMLでは証明書の期限より、鍵を定期的に替える運用のほうが効果的です。Keycloakの公式ガイドは3〜6か月ごとの鍵の作成を目安にしており、SPへ新旧2枚の証明書を先に配ってから切り替える手順を組んでおくと、期限の長さに関係なく停止を避けられます。
SAMLのIdPとOIDCのプロバイダーは別に用意する必要がありますか?
Keycloakや主なIDaaSは、1つの基盤でSAMLとOIDCの両方を提供できるため、別々に建てる必要はありません。既製のSaaSにはSAML、自社開発のWebアプリやスマホアプリにはOIDCというように、SPごとにプロトコルを選んで同じ利用者情報でログインさせる構成が組めます。SimpleSAMLphpはSAMLが中心なので、OIDCも使う予定ならKeycloakを選びます。
関連記事
- SAMLとは?認証フロー・IdP/SPの仕組みとOAuth・OIDCとの使い分けを実装視点で解説:SAML全体の仕組みと認証フロー
- IDaaSとは?SSO・IdPとの違いとSAML・OIDC・SCIM連携を実装視点で解説:IdPをIDaaSで調達する場合の比較
- SAMLメタデータとは:EntityDescriptorの読み方と署名証明書の更新手順:IdPメタデータの読み方とSP側の証明書更新
- SAML NameIDFormatとメタデータの不一致でSSOが失敗する原因の切り分け:SP登録後にSSOが失敗したときの切り分け
- SAML連携の実装手順:自社SaaSにテナント別IdP設定とJITを組み込む:SP側で取引先のIdPを受け入れる実装