本文へスキップ

SAML SSO フロー図:SP 起点と IdP 起点のログイン

SAML 2.0 のシングルサインオンの仕組みを解説。SP 起点と IdP 起点のフロー、SAML レスポンスの中身、アプリが確認すべきこと、壊れやすい箇所。

· 創業者・開発者

公開日:

ユーザー、ブラウザー、サービスプロバイダー、ID プロバイダーのシーケンス図です。アプリは AuthnRequest を付けて IdP にリダイレクトします。任意のフラグメントがログインと MFA を示します。IdP は SAMLResponse を ACS URL に POST するフォームを返し、アプリはそれを検証して Cookie を設定します。

契約を結んだ最初の大口顧客は、自社の ID プロバイダーとのシングルサインオンを求めてきます。そしてその多くは SAML 2.0 です。この標準は 2005 年のもので、ブラウザーのリダイレクトとフォーム送信で XML を運び、エラーメッセージは曖昧なものから無言のものまでさまざまです。深刻なセキュリティホールを含め、SAML のバグの多くは、どのメッセージが正確にどこへ行き、サービスプロバイダーが何を確認すべきかを知らないことから生まれます。

この記事では、SP 起点のフローをシーケンス図でメッセージごとに追い、続いて IdP 起点のバリエーションとそのリスク、SAML レスポンスの構造、アプリのための検証チェックリスト、メタデータと証明書のローテーション、そして SAML と OpenID Connect の比較を説明します。

3 つの当事者

当事者 SAML の用語 例
ユーザーのブラウザー ユーザーエージェント 任意のブラウザー
自社のアプリケーション サービスプロバイダー(SP) 自社の SaaS アプリ
顧客のログインシステム ID プロバイダー(IdP) Microsoft Entra ID、Okta、Google Workspace、AD FS

一般的な Web Browser SSO プロファイルでは、SP と IdP が直接通信することはありません。すべてのメッセージは、リダイレクトか自動送信されるフォームとしてブラウザーを経由します。両者が共有するのは、メタデータとして一度だけ交換する設定です。双方の entity ID、IdP のサインイン URL、SP の Assertion Consumer Service(ACS)URL、IdP の署名用証明書です。

SP 起点のフローを順に追う

ユーザーはアプリから始めます。

  1. ユーザーがセッションなしで https://app.example.com/dashboard を開きます。
  2. アプリは、一意の ID、自身の entity ID、ACS URL を持つ AuthnRequest を作成し、その ID を保留中のログイン状態に保存して、ブラウザーを IdP にリダイレクトします。HTTP-Redirect バインディングでは、リクエストはクエリ文字列の SAMLRequest(deflate 圧縮して base64 エンコードしたもの)として送られ、ユーザーが行きたかった場所を覚えておく RelayState の値が添えられます。
  3. IdP がユーザーを認証します。パスワードと MFA、または既存の IdP セッションです。後者の場合、ユーザーには何も表示されません。
  4. IdP は、署名付きの SAMLResponse と同じ RelayState を ACS URL に POST するフォームを含む HTML ページを返します。これが HTTP-POST バインディングで、小さなスクリプトがフォームを自動送信します。
  5. アプリはレスポンスを検証し、自身のセッション Cookie を作成して、RelayState に保存されたページにユーザーをリダイレクトします。

それ以降、ユーザーが誰かを決めるのはアプリ自身のセッションです。SAML が再び関わるのは、そのセッションが切れたときだけです。

バインディング仕様では RelayState は 80 バイトまでに制限されています。実際の戻り先 URL はサーバー側に保存し、短いキーを送りましょう。

SAML レスポンスの中身

アプリが読む部分だけに絞ると、レスポンスは次のようになります。

<samlp:Response ID="_r81c" InResponseTo="_req42" Destination="https://app.example.com/saml/acs">
  <saml:Assertion ID="_a77e">
    <saml:Issuer>https://idp.customer.com/entity</saml:Issuer>
    <ds:Signature>…</ds:Signature>
    <saml:Subject>
      <saml:NameID>ana@customer.com</saml:NameID>
      <saml:SubjectConfirmationData InResponseTo="_req42"
        Recipient="https://app.example.com/saml/acs" NotOnOrAfter="2026-11-02T09:05:00Z"/>
    </saml:Subject>
    <saml:Conditions NotBefore="2026-11-02T08:59:30Z" NotOnOrAfter="2026-11-02T09:05:00Z">
      <saml:AudienceRestriction><saml:Audience>https://app.example.com</saml:Audience></saml:AudienceRestriction>
    </saml:Conditions>
    <saml:AttributeStatement>…email, name, groups…</saml:AttributeStatement>
  </saml:Assertion>
</samlp:Response>

アサーションは署名付きの表明です。ユーザーが誰か(NameID)、誰がそう言っているか(Issuer)、誰に向けたものか(Audience)、有効期間、そしてどのリクエストへの応答か(InResponseTo)を示します。

検証チェックリスト

SAML のセキュリティが成功するか失敗するかは、ACS エンドポイントで決まります。ACS エンドポイントは次のことを行わなければなりません。

  1. 署名は IdP のメタデータにある証明書で検証します。 メッセージ自体に埋め込まれた証明書を使ってはいけません。
  2. 署名の対象になっている部分だけを読みます。 XML 署名ラッピング攻撃は、署名のない 2 つ目のアサーションを差し込み、コードがある要素を検証して別の要素を読むことを狙います。研究者たちは 2012 年に、主要な SAML フレームワーク 14 個のうち 11 個でこの欠陥を発見しました。そしてこの問題は繰り返し現れています。ruby-saml は 2024 年に CVSS 10.0 の署名回避(CVE-2024-45409)を、2025 年にはパーサー間の差異による 2 つの回避(CVE-2025-25291 と CVE-2025-25292)を修正しました。
  3. Issuer を、この顧客の IdP の entity ID と照合します。
  4. Audience を、自分の entity ID と照合します。
  5. Destination と Recipient を、自分の ACS URL と照合します。
  6. NotBefore と NotOnOrAfter を、時計のずれに備えて 1〜2 分の許容幅を持たせて確認します。
  7. InResponseTo を、このブラウザー向けに発行し、まだ使われていない AuthnRequest の ID と照合します。
  8. リプレイを拒否します。 アサーション ID を有効期限が切れるまで記録しておきます。
  9. RelayState は信頼できない入力として扱います。 リダイレクト先は相対パスか許可リストに限定してください。そうしないとオープンリダイレクトになります。

10 年にわたる SAML の脆弱性から得られる教訓はこうです。メンテナンスされているライブラリを使い、バージョンを固定し、すぐにパッチを当て、XML 署名の検証を自作しないことです。

IdP 起点の SSO と、それがより危険な理由

IdP 起点のフローでは、ユーザーは IdP から始めます。たいていは Okta や Microsoft My Apps ポータルのようなダッシュボードで、アプリのタイルをクリックします。IdP は、要求されていないレスポンスを ACS URL に POST します。AuthnRequest がなかったので、照合すべき InResponseTo もありません。

SP 起点 IdP 起点
開始地点 自社のアプリ IdP のポータル
AuthnRequest あり なし
InResponseTo の確認 あり 不可能
ログイン CSRF への防御 組み込み 追加の注意が必要
ディープリンク RelayState で可能 通常は既定のページに着地する

InResponseTo がないと、有効なレスポンスを入手した攻撃者や、被害者のブラウザーにレスポンスを POST させた攻撃者は、被害者を攻撃者のアカウントにログインさせたり、盗んだレスポンスを有効期間内にリプレイしたりできます。顧客がダッシュボードのタイルを必要とする場合、より安全なのは、アプリのログイン URL を開くタイルにして、そこから通常の SP 起点のフローを始めるパターンです。要求されていないレスポンスを受け付けざるを得ない場合は、有効期間を短くし、アサーション ID を 1 回限りに強制し、全体ではなく顧客ごとに許可してください。

メタデータ、証明書、マルチテナント構成

顧客ごとの IdP は、それぞれ独立した信頼関係です。テナントごとに、IdP の entity ID、サインイン URL、署名用証明書、属性マッピングを保存します。このテナントごとの設定が通常どこに置かれるかは、マルチテナント SaaS アーキテクチャ図で示しています。

署名用証明書には有効期限があり、期限切れや差し替えられた証明書は、顧客全体が突然サインインできなくなる最も一般的な原因です。切り替え期間中は IdP ごとに 2 つの証明書を受け入れ、IdP がメタデータ URL を公開しているなら定期的に読み込み、期限切れの数週間前にアラートを出しましょう。

SAML は認証を行いますが、ユーザー一覧の同期は行いません。初回ログイン時にアカウントを作るのは簡単ですが、退職した人のアクセスを取り消すにはプロビジョニングが必要で、通常は SCIM 2.0(RFC 7644)を使います。これがないと、無効化された従業員も、アプリがすでに発行したセッションを使い続けられます。

SAML か OpenID Connect か

SAML 2.0 OpenID Connect
形式 XML 署名付きの XML アサーション JSON と JWT の ID トークン
伝送 ブラウザーのリダイレクトとフォーム送信 リダイレクトとバックチャネルでのトークンリクエスト
モバイルアプリと SPA 扱いにくい それらのために設計されている
典型的な用途 SaaS アプリへのエンタープライズ SSO コンシューマー向けログイン、API、最新のエンタープライズ SSO

新しいアプリは OpenID Connect から始めてもかまいませんが、企業の顧客は今後も SAML を求めてくるので、ほとんどの B2B 製品は両方に対応しています。OpenID Connect の土台となるプロトコルは、OAuth 2.0 フロー図で扱っています。

SAML SSO の図

ユーザー、ブラウザー、サービスプロバイダー、ID プロバイダーのシーケンス図です。アプリは AuthnRequest を付けて IdP にリダイレクトします。任意のフラグメントがログインと MFA を示します。IdP は SAMLResponse を ACS URL に POST するフォームを返し、アプリはそれを検証して Cookie を設定します。

6 個のコンポーネント · 11 個の接続

Archboard で開く
ダイアグラムの内容
  • User
  • Browser
  • Service provider (your app)
  • Identity provider
  • no IdP session yet
  • 1. Open the app
  • 2. GET /dashboard (no session)
  • 3. 302 to IdP: SAMLRequest + RelayState
  • 4. GET /sso?SAMLRequest=…&RelayState=… (HTTP-Redirect)
  • 5. Login page
  • 6. Password + MFA
  • 7. HTML form: signed SAMLResponse + RelayState
  • 8. POST /saml/acs (HTTP-POST, auto-submit)
  • 9. 302 /dashboard + session cookie
  • 10. GET /dashboard (cookie)
  • 11. 200 OK
SP 起点の SAML シングルサインオンです。アプリは AuthnRequest を付けてブラウザーを ID プロバイダーにリダイレクトします。IdP はユーザーをサインインさせ、署名付きレスポンスをアプリの ACS URL に POST します。アプリはそれを検証してから自分のセッションを作成します。

図の読み方は次のとおりです。

  • 4 つの参加者。 ユーザー、ブラウザー、サービスプロバイダーとしてのアプリ、ID プロバイダーです。時間は下に向かって流れます。
  • リダイレクトは、AuthnRequest と RelayState を、ブラウザーを経由してアプリから IdP に運びます。
  • 任意のフラグメントはログインのステップです。既存の IdP セッションがあればスキップされ、ユーザーには一瞬のリダイレクトしか見えません。
  • フォーム送信は、署名付きの SAMLResponse を ACS URL に届けます。アプリはそれを検証し、自身のセッション Cookie を設定します。
  • Markdown ブロックには、ACS エンドポイントが行うべき確認をまとめています。

デバッグに役立つ SSO フローを描くには

  • ブラウザーを独立した参加者として描きます。 SAML はフロントチャネルのプロトコルです。ブラウザーを隠すと、メッセージが傍受やリプレイされうる場所も隠れてしまいます。
  • 各矢印にバインディングを書きます。 リダイレクトか POST か、そしてパラメーター名です。設定ミスの大半は、URL かバインディングの誤りです。
  • メッセージに ID を書きます。 InResponseTo がリクエストの ID を返していることを示せば、なぜそれを確認するのかが伝わります。
  • IdP 起点のフローは別の図に描き、メインの図の分岐にはせず、リスクを書き添えます。

参加者、フラグメント、ラベルについてはシーケンス図の例で詳しく扱っています。より広いアクセスモデルの中で ID プロバイダーがどこに位置するかは、ゼロトラストアーキテクチャ図を参照してください。

Archboard で作る

  1. Google または GitHub でサインインし、図を作成します。
  2. ユーザー用にシーケンスパックの アクター を追加し、続いて ライフライン ノードを 3 つ追加します。Browser、Service provider、Identity provider です。
  3. メッセージを上から下へ、ライフライン間のエッジとして描きます。最初のリクエスト、SAMLRequest と RelayState を付けたリダイレクト、IdP へのリクエスト、SAMLResponse を含むフォーム、ACS URL への POST、そしてセッション Cookie を伴う最後のリダイレクトです。
  4. ログインと MFA のステップを囲むように、演算子を opt にしたシーケンスパックの 複合フラグメント を追加します。
  5. 検証チェックリストを書いた基本パックの Markdown ブロックを追加します。

この例で使うダイアグラムノードは 6 個で、Free プランの範囲に収まります。

よくある質問

SP 起点と IdP 起点の SSO の違いは何ですか? SP 起点のログインはアプリから始まり、アプリが IdP に AuthnRequest を送ります。IdP 起点のログインは IdP のポータルから始まり、IdP が要求されていないレスポンスをアプリに POST します。後者には照合すべきリクエストがないため、リプレイやログイン CSRF への防御が弱くなります。

ACS URL とは何ですか? Assertion Consumer Service URL は、SAML レスポンスを HTTP POST で受け取るアプリ側のエンドポイントです。IdP で設定し、各レスポンスの Destination と Recipient の値と一致していなければなりません。

RelayState は何に使われますか? ユーザーが行きたかった場所を運び、ログイン後にアプリがそこへ案内できるようにします。80 バイトまでに制限されており、信頼できない入力なので、サーバー側の状態を指すキーとして使うか、許可リストで検証してください。

SAML は時代遅れですか? OpenID Connect より古く、安全に実装するのも難しいですが、多くの企業向け ID プロバイダーや購買担当者が期待する標準であり続けています。ほとんどの B2B 製品は両方に対応しています。

SAML は退職したユーザーを削除してくれますか? いいえ。SAML が認証するのはログイン時だけです。プロビジョニングの解除には、SCIM などのプロビジョニング連携と、アプリ側での妥当なセッション有効期間が必要です。

無料で始める — 1 シート、クレジットカード不要。

関連記事

← ブログに戻る

1分でキャンバスを開く

Google または GitHub でサインイン。1シートは無料で、チームが加わったらアップグレード。