Skip to content
Published on

レガシー WebSSO とモダン IAM の共存設計 — ハイブリッド移行期のアーキテクチャパターン

Authors

はじめに — 移行期はイベントではなく時代である

前の2つの記事で SiteMinder のアーキテクチャと Keycloak へのマイグレーション戦略を扱いました。今回のテーマは、その間に横たわる最も長い区間、ハイブリッド共存期です。

まず現場の真実を一つ認めるところから始めましょう。数百のアプリを持つ組織では、レガシー WebSSO とモダン IAM の共存期間は四半期単位ではなく年単位です。2〜5年が普通で、もっと長引く組織も珍しくありません。だとすれば、共存は「しばらく耐える一時的な状態」ではなく、それ自体が設計・運用・監視されるべきアーキテクチャです。2026年の視点で見ればなおさらです — 移行期の間も passkeys の導入、Zero Trust ポリシー、AI エージェントのアイデンティティ管理といった新しい要件は止まることなく入ってくるからです。

本記事は、共存期を耐えるのではなく、うまく設計して通過する方法を扱います。4つの核心パターン、セッションとログアウトの同期問題、セキュリティリスク、そして「いつ終わったと宣言できるのか」の基準まで整理します。

共存期の地形図

まず、典型的なハイブリッド環境の全体像です。

                         ユーザー (ブラウザ)
                               |
              +----------------+----------------+
              |                                 |
              v                                 v
   +--------------------+            +--------------------+
   |  モダン IAM 領域     |   ブリッジ   | レガシー WebSSO 領域 |
   |   (Keycloak)       | <========> |  (SiteMinder)      |
   |                    | SAML/OIDC  |                    |
   |  - OIDC アプリ      |            |  - SM_USER ヘッダー  |
   |  - SPA + API       |            |  - エージェント保護   |
   |  - passkeys        |            |  - FCC ログイン      |
   +---------+----------+            +---------+----------+
             |                                 |
             v                                 v
   +--------------------+            +--------------------+
   | リバースプロキシ層     |            |  共有 User Store    |
   | (oauth2-proxy など) |----------->|  (AD/LDAP)         |
   | レガシーアプリを無修正 |            |  両側が共に参照       |
   | で収容              |            |                    |
   +--------------------+            +--------------------+

この図から導かれる設計原則が3つあります。

  1. ユーザーストアは可能な限り一つに: アカウント/パスワードの信頼できる情報源が二つに割れた瞬間、共存期の難易度は倍になります。
  2. 認証の信頼できる情報源は段階的に片側へ: 2つの IdP がそれぞれログイン画面を持つ期間を最小化し、片方がもう片方に委譲する構造を早く作ります。
  3. すべてのブリッジは期間限定であることを明示: ブリッジインフラには作成日と廃止目標日をあわせて記録します。

パターン 1: 標準プロトコルブリッジ(SAML/OIDC フェデレーション)

最も正攻法のパターンです。両システムが話せる標準プロトコル(現実的には SAML 2.0)で双方向の信頼を構成します。

  • レガシーを IdP に: 移行初期。既存のログイン体験を維持し、新規アプリだけ Keycloak(ブローカー)経由で開発。
  • モダンを IdP に: 移行中後期。ログイン自体を Keycloak に移し、レガシー領域は SAML SP に格下げ。

このパターンの決定的な利点は、両側ともベンダーサポート範囲内の標準機能しか使わないことです。カスタムコードがほとんどないため、運用リスクが低い。

移行中後期(モダンが IdP)の認証フローは次のとおりです。

ユーザー → レガシーアプリにアクセス
  → SM Agent: SMSESSION なし → SiteMinder フェデレーション SP が開始
  → SAML AuthnRequest → Keycloak
  → Keycloak: SSO セッションがあれば即時、なければログイン (passkey 可)
  → SAML Response (assertion) → SiteMinder
  → SiteMinder: assertion 検証 → SMSESSION 発行
  → ユーザー: レガシーアプリへ (SM_USER ヘッダーは従来どおり)

運用上の注意点は次のとおりです。

  • 属性マッピング契約の文書化: assertion にどの属性(社員番号、グループ、メール)をどの形式で載せるかは、両システム間の API 契約です。片側の不用意な変更が反対側のアプリ全体を壊します。
  • 証明書ライフサイクル管理: SAML 署名証明書の期限切れは、ハイブリッド環境の定番の全面障害原因です。期限の 90/30/7 日前のアラートを自動化してください。
  • クロックスキュー: assertion の NotBefore/NotOnOrAfter 検証は、両側サーバーの NTP 同期が前提です。

パターン 2: リバースプロキシのヘッダー注入 — レガシーアプリを無修正で保護

修正不可能なヘッダーベースのアプリをモダン IdP の配下へ移すパターンです。構成は前回の記事で扱ったので、ここでは共存観点のポイントとともに最小構成を再掲します。

# nginx: レガシーアプリ前段の認証プロキシ
server {
  listen 443 ssl;
  server_name legacy-crm.example.com;

  location /oauth2/ {
    proxy_pass http://oauth2-proxy:4180;
  }

  location / {
    auth_request /oauth2/auth;
    error_page 401 = /oauth2/start?rd=$scheme://$host$request_uri;

    auth_request_set $user $upstream_http_x_auth_request_preferred_username;
    auth_request_set $groups $upstream_http_x_auth_request_groups;

    # レガシーアプリが期待する正確なヘッダー名へ変換して注入
    proxy_set_header SM_USER $user;
    proxy_set_header SM_USERGROUPS $groups;
    proxy_pass http://legacy-crm.internal:8080;
  }
}
# oauth2-proxy 核心設定 (Keycloak OIDC)
provider: keycloak-oidc
client_id: legacy-crm-proxy
oidc_issuer_url: https://keycloak.example.com/realms/enterprise
cookie_secure: true
cookie_samesite: lax
session_store_type: redis      # 複数インスタンス時のセッション共有
redis_connection_url: redis://redis.internal:6379
skip_provider_button: true

共存の観点でこのパターンの本当の意味は、アプリの所属をルーティングで変えられることです。DNS がレガシーゲートウェイを指せばそのアプリはレガシー領域所属、プロキシを指せばモダン領域所属です。アプリのコードはどちらでも同一なので、切り替えもロールバックもルーティング変更一回です。

注意点は3つです。

  • プロキシのセッションストア: 複数インスタンスのプロキシは必ず Redis のような共有セッションストアを使うこと。さもないとロードバランシング時にランダムな再ログインが発生します。
  • WebSocket と長寿命接続: レガシーアプリが WebSocket や SSE を使うなら、auth_request 経路のタイムアウト/アップグレード設定を別途検証する必要があります。
  • 大きなアップロード: auth_request はボディなしで認証だけ確認しますが、プロキシのバッファリング設定によって大容量アップロードが壊れる事例があります。

パターン 3: Identity Orchestration レイヤー

ブリッジとプロキシをアプリごとに手作業で構成する代わりに、抽象化レイヤーがアイデンティティフロー全体をポリシーで管理するパターンです。Strata Maverics が代表例で、クラウドベンダーの類似機能もあります。

            +------------------------------------------+
            |        Identity Orchestration Layer       |
            |  アプリ別ポリシー: 「このアプリは Keycloak、    |
            |  あのアプリは SM」; セッション変換 /           |
            |  ヘッダー注入 / ログアウトファンアウト処理        |
            +-----+--------------------+----------------+
                  |                    |
                  v                    v
            +-----------+        +-----------+
            | Keycloak  |        | SiteMinder|
            +-----------+        +-----------+

利点は明確です。

  • アプリ単位の IdP 切り替えが設定変更になり、ウェーブ切り替えの速度が上がります。
  • 複数レガシー IdP 環境(M&A で SiteMinder と Oracle Access Manager が両方ある場合など)で威力が大きい。
  • ログアウトのファンアウト、セッション寿命の整合といった厄介な問題を製品機能として吸収します。

欠点も明確です。

  • ライセンスコストがかかり、アイデンティティ経路の全トラフィックが通過する新しい単一障害点が生まれます — HA 設計が必須です。
  • オーケストレーションレイヤー自体への依存が生じます。移行完了後にこのレイヤーを撤去できる出口設計を最初から含めるべきです。

エンジニアリング余力が十分な組織は、パターン 1+2 の組み合わせの自前構築で同じ効果を出せます。要するにこのパターンは技術ではなく、調達と能力の意思決定です。

パターン 4: Strangler Fig — アプリ単位の漸進移行

Strangler Fig はマーティン・ファウラーが整理したレガシーモダナイゼーションの古典パターンで、絞め殺しの木が宿主を徐々に包み込んで置き換えるように、新システムが旧システムをアプリ単位で侵食していくアプローチです。IAM 共存期に適用すると次のようになります。

 時点 t0:  [SM: 200 アプリ]                  [KC: 0]
 時点 t1:  [SM: 180 アプリ] --Wave 1(20)--> [KC: 20]
 時点 t2:  [SM: 120 アプリ] --Wave 2〜3----> [KC: 80]
 時点 t3:  [SM: 30 アプリ]  --Wave 4〜6----> [KC: 170]
 時点 t4:  [SM: 0. デコミッション]            [KC: 200]

核心ルールは3つです。

  1. 新規アプリは必ず新システムへ: レガシー領域への新規参入を禁止するポリシー(architecture fitness function)をガバナンスとして強制します。これがなければ侵食ではなく永久共存になります。
  2. 移行単位はユーザージャーニーの束で: ユーザーが一つの業務フローで行き来するアプリ群を同じウェーブにまとめると、ブリッジ経由回数が減り体感品質が良くなります。
  3. 進捗を公開指標に: 残りアプリ数、レガシー経由認証比率をダッシュボードで公開すると、組織の推進力が維持されます。

セッション寿命とログアウト同期 — 共存期最大の難題

セッション階層の不一致

ハイブリッド環境では、一人のユーザーの「ログイン状態」は最低3つの階層に存在します。

階層セッション寿命の制御
モダン IdPKeycloak SSO セッションSSO Session Idle/Max
ブリッジ/プロキシoauth2-proxy クッキー、SAML セッションクッキー期限、トークンリフレッシュ
レガシーSMSESSIONrealm idle/max タイムアウト

この3階層の寿命がずれると、2つの症状が現れます。

  • ゴーストセッション: 上位階層(IdP)は死んでいるのに下位階層(プロキシクッキー、SMSESSION)が生きていて、「ログアウトしたのにまだ入れる」というセキュリティ問題。
  • 再認証ストーム/リダイレクトループ: 下位が先に死に、上位のリフレッシュが絡まると、ユーザーが無限リダイレクトに遭う可用性の問題。

実務の推奨は単純なルールに収斂します。寿命は上から下に行くほど同じか短く。 IdP セッションが最も長く、ブリッジセッションはそれ以下、レガシーセッションが最も短ければ、失効時は常に上位へ遡って静かに再発行される安全な方向に動作します。

ログアウト同期

ログアウトはさらに困難です。ユーザーがどちら側でログアウトしても、すべての階層のセッションが終了しなければなりません。標準的な道具は次のとおりです。

現実的な解決策は中央ログアウトオーケストレーションエンドポイントです。すべてのアプリのログアウトボタンがこのエンドポイントを指すようにし、ここで各階層のログアウトを順次実行します。

GET /global-logout
  1. SiteMinder ログアウト呼び出し (SMSESSION 無効化)
  2. oauth2-proxy セッション削除 (/oauth2/sign_out)
  3. Keycloak RP-Initiated Logout へリダイレクト
     (id_token_hint + post_logout_redirect_uri)
  4. 「ログアウトしました」ランディングページ

バックチャネルが可能な区間(Keycloak とプロキシの間)は back-channel logout で補強し、不可能な区間(SMSESSION)はフロントチャネルのチェーンで処理する混合構成が普通です。全階層ログアウトの完全性は、パターンの選択にかかわらず必ずテストシナリオに含めるべきです。

ユーザー体験 — 二重ログインの防止

共存期の UX の失敗は、すなわちヘルプデスクの殺到です。点検項目を整理します。

  • ログイン画面は一つだけ: ユーザーが2種類のログイン画面にランダムに遭遇する状態は最短期間で終わらせるべきです。ブリッジを通じて認証の信頼できる情報源を片側に集めることが最優先課題である理由です。
  • ディープリンクの保存: ブリッジを経由するすべてのフローで、ユーザーが最初にリクエストした URL(RelayState、rd パラメータ)が保存されるか検証します。「ログインしたらホームに飛ばされた」は些細に見えて最も多くの不満を生みます。
  • セッション失効の優雅な処理: AJAX リクエストが失効に遭ったら 302 の HTML ではなく 401 の JSON を受け取るよう、プロキシに分岐設定を置きます。さもないと SPA 画面の真ん中にログインフォームの HTML が刺さる古典的バグが生まれます。
  • 切り替え告知: ウェーブ切り替え日に該当アプリのユーザーへ「本日からログイン画面が変わります」と告知するだけで、チケットが半分以下に減ります。

セキュリティリスク — 共存期に拡大する攻撃面

共存期は本質的に、攻撃面が両システムの和集合 + ブリッジに拡大する期間です。

ヘッダースプーフィング

プロキシパターンの慢性的リスクです。防御チェックリストは次のとおりです。

[ ] レガシーアプリはプロキシ/ゲートウェイからのみ到達可能 (ネットワークポリシー)
[ ] プロキシはインバウンドの SM_USER 系ヘッダーを無条件に上書き
[ ] ゲートウェイ-アプリ区間に mTLS または共有シークレットヘッダー
[ ] アプリ直接ポート(8080 など)が社内網からでも開いていないか定期スキャン
[ ] ヘッダー偽造の試行をテストシナリオ(T7)として常時自動化

特に最後の項目が重要です。共存期はネットワーク変更が頻繁なため、最初に塞いだ直接アクセス経路がいつの間にか再び開いている事故が実際に起こります。

セッション不一致の悪用

ゴーストセッションは UX 問題でありセキュリティ問題でもあります。退職者のアカウントを IdP で無効化しても、生きている SMSESSION やプロキシクッキーでしばらくアクセスできるなら、それは監査指摘事項です。対策は次のとおりです。

  • レガシー/ブリッジのセッション寿命を短く保つ(上記「下に行くほど短く」のルール)
  • アカウント無効化イベントをフックで受け、関連セッションを能動的に破棄(Keycloak Event Listener SPI + SiteMinder Session Store 連携)
  • 高リスクアプリはセッション検証時に毎回ユーザー状態を再確認するオプションを適用

ブリッジ自体のセキュリティ

  • SAML ブリッジ: 署名検証の未設定、弱いダイジェストアルゴリズム、audience の未検証が定番の脆弱性です。assertion の暗号化と署名アルゴリズムを点検してください。
  • プロキシ: クッキーシークレットのローテーション、Redis セッションストアのアクセス制御、トークンをログに残さない設定を確認します。RFC 9700 の勧告をベースラインにすると良いでしょう。

モニタリングと移行完了基準

共存期の核心指標

指標意味目標の方向
レガシー経由認証比率全認証のうち SiteMinder が処理した割合単調減少
ブリッジ認証比率ブリッジを経由したクロス領域認証の割合中盤に増加後、減少
認証成功率 (両側それぞれ)可用性ベースライン99.9 以上を維持
ログイン p95 所要時間ブリッジホップ追加による遅延の監視ベースライン + 1秒以内
未移行アプリ数strangler の進捗ウェーブ計画に対して追跡
ゴーストセッション検知数ログアウトの不完全性0 へ収束

ログの統合も重要です。SiteMinder の監査ログと Keycloak のイベントを同じ SIEM に集め、ユーザー単位で両側の認証履歴を一つのタイムラインとして見られるようにしなければ、共存期のインシデント調査は実質的に不可能です。

# Keycloak イベントを SIEM へ送る構成の概念 (例: syslog リスナー)
spi-events-listener-jboss-logging-success-level: info
spi-events-listener-jboss-logging-error-level: warn
# 本番ではカスタム EventListener SPI で Kafka/SIEM へ送信するのが一般的

移行完了を宣言する基準

「終わった」という宣言には客観的基準が必要です。推奨基準は次のとおりです。

  1. レガシー経由認証比率がゼロになり、N 週間(例: 8週間)維持される — 月末/四半期末の周期業務を含む
  2. 未移行アプリ 0 件 — または残存アプリの正式な廃止決定の完了
  3. レガシー依存の DNS/ルーティング項目 0 件
  4. ブリッジ/オーケストレーションレイヤーの撤去計画の承認 — 共存インフラも一緒に撤去してこそ本当の完了です
  5. SiteMinder ポリシーアーカイブの保存とライセンス終了処理
  6. 監査ログ保存義務の移管(規制業種の場合、数年分のレガシー監査ログ保存方針の確定)

事例シナリオ — 仮想の銀行イントラネット 200 アプリ移行

最後に、パターンを総合した仮想事例です。設定は次のとおり: 中堅銀行、イントラネットアプリ 200 個、SiteMinder 12.8(EOS 目前)、従業員 1万2千人、目標期間 30 か月。

インベントリ結果: 標準プロトコルアプリ 25 個(A)、修正可能ヘッダーアプリ 60 個(B)、修正不可ヘッダーアプリ 105 個(C)、SDK 依存アプリ 10 個(D)。

アーキテクチャ決定:

  • Keycloak HA クラスタを構築し、既存 AD を User Federation で接続(全パターン共通の基盤)
  • フェーズ 1(0〜6か月): Keycloak をブローカー、SiteMinder を IdP に(パターン 1、方向 2)。新規アプリ凍結ポリシーを施行 — すべての新規アプリは Keycloak OIDC のみ(パターン 4、ルール 1)
  • フェーズ 2(6〜12か月): ログインの信頼できる情報源を逆転 — Keycloak が IdP、SiteMinder は SAML SP に格下げ(パターン 1、方向 1)。この時点から全従業員が passkey を登録可能
  • フェーズ 3(6〜24か月、並行): A グループ 25 個はエンドポイント差し替えで即時移行。B グループ 60 個は定期リリースに OIDC 転換を織り込んで漸進移行。C グループ 105 個は oauth2-proxy ファーム(パターン 2)でウェーブあたり 15〜20 個ずつ移行
  • フェーズ 4(24〜30か月): D グループ 10 個の個別再設計 — 3 個は再開発、5 個はプロキシ収容可能と判明、2 個は廃止決定。SiteMinder のデコミッション

遭遇した問題(現実でもそのまま出会うであろうもの):

  • Wave 3 で C グループの一つのアプリが SM_USERGROUPS のキャレット区切りを LDAP DN 形式と一緒にパースしており、グループマッピングの全面再作業に — ヘッダーの「値の形式」までインベントリに含めるべきという教訓
  • 月末決算バッチがサービスアカウントでヘッダー認証を模倣していたことが発覚 — 人間以外のアイデンティティ(non-human identity)は別トラックに分離し、client credentials フローへ転換
  • SAML 証明書の期限切れでフェーズ 2 中に 47 分間の全面ログイン障害 — 以後、証明書期限アラートと二重化キーのロールオーバー手順を導入
  • 30 か月時点で残存アプリ 4 個 — オーナー部門への課金ガバナンスが発動し、3 か月以内に整理完了

結果的に 33 か月でデコミッションを完了し、共存期の間にも passkey 導入(14 か月目)と Zero Trust ネットワークポリシーの適用を並行できました。共存アーキテクチャを正しく設計したからこそ、モダナイゼーションが移行完了を待つ必要がなかった — これがこのシナリオの要点です。

パターン比較サマリーと選択ガイド

4つのパターンは排他的ではなく組み合わせるものですが、意思決定の助けとして一つの表で比較します。

項目パターン 1 ブリッジパターン 2 プロキシパターン 3 オーケストレーションパターン 4 strangler
解決する問題IdP 間の SSO 接続無修正アプリの収容移行の複雑さの吸収移行の方向性/ガバナンス
構築難易度低(標準機能)中(アプリ別構成)低(購入)技術でなく組織の問題
運用リスク証明書/属性契約プロキシ SPOF、セッションストア新たな SPOF、ベンダーロックイン推進力の喪失
コスト人件費中心インフラ + 人件費ライセンスガバナンスコスト
適用時期共存期の全区間C 群アプリの移行区間スケジュール厳しい/マルチ IdP共存期の全区間

簡単な選択フローは次のとおりです。

開始
 ├─ 2つの IdP 間の SSO が必要か? ──────────→ 常にパターン 1 (基盤)
 ├─ 修正不可のヘッダーアプリがあるか? ─ はい ─┐
 │                                       ├─ エンジニアリング余力十分 → パターン 2 自前構築
 │                                       └─ 余力不足/マルチ IdP    → パターン 3 導入を検討
 └─ アプリが 30 個以上か? ── はい ──────────→ パターン 4 のガバナンス必須

トラブルシューティング — 共存期によく遭遇する障害

最後に、ハイブリッド環境の運用中に実際によく報告される症状と一次対応を整理します。

症状 1: 特定アプリでのみ間欠的に再ログインを要求される

  • よくある原因: プロキシの複数インスタンスが共有セッションストアなしで動作している(ロードバランサーが別インスタンスに振るたびにセッションミス)、または Agent Key ロールオーバー直後の SMSESSION 検証失敗。
  • 一次確認: 該当アプリ経路のプロキシインスタンス数と session_store_type 設定、レガシー側なら Policy Server のキーロールオーバー時刻と障害時刻の相関。

症状 2: リダイレクトループ

  • よくある原因: 階層間のセッション寿命の逆転(プロキシクッキーは生きているのに IdP セッションが失効)、または redirect_uri とクッキードメインの不一致。
  • 一次確認: ブラウザの開発者ツールで 302 チェーンをキャプチャし、どの階層がどの階層へ投げているかを特定します。curl での再現は次のとおりです。
# リダイレクトチェーンの追跡 (クッキー保持、最大10ホップ)
curl -s -o /dev/null -L --max-redirs 10 \
  -b cookies.txt -c cookies.txt \
  -w '%{num_redirects} redirects, final: %{url_effective}\n' \
  https://legacy-crm.example.com/

# 各ホップの Location ヘッダーを確認
curl -s -D - -o /dev/null https://legacy-crm.example.com/ | grep -i '^location'

症状 3: ログアウトしたのに別のアプリにまだアクセスできる

  • よくある原因: ログアウトオーケストレーションチェーンでの一階層の漏れ(特に SMSESSION 無効化のステップ)、または back-channel logout を受信できないクライアント。
  • 一次確認: グローバルログアウト後、階層別のクッキー状態を順にダンプします。SMSESSION、プロキシクッキー、Keycloak セッションクッキーのどれが生き残るかが、漏れの箇所を教えてくれます。

症状 4: 切り替え後にグループ/権限が空に見える

  • よくある原因: グループクレームの形式不一致(キャレット区切り文字列 vs JSON 配列)、クレームの欠落(scope に groups が未包含)、または LDAP group mapper の同期遅延。
  • 一次確認: まずトークン自体をデコードしてクレームの有無を確認し、あればプロキシのヘッダー変換を、なければ Keycloak の mapper 設定を見ます。
# access token のクレーム確認 (jq 使用)
TOKEN=$(curl -s -X POST \
  -d "client_id=debug-cli" -d "grant_type=password" \
  -d "username=testuser" -d "password=..." \
  https://keycloak.example.com/realms/enterprise/protocol/openid-connect/token \
  | jq -r .access_token)
echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq '.groups'

症状 5: 月末/四半期末にだけ発生する認証失敗

  • よくある原因: バッチ/サービスアカウントが人間用の認証経路(ヘッダー模倣、画面スクレイピング)を使っていて、移行で漏れたケース。
  • 一次対応: non-human identity の全数調査トラックを別途設け、client credentials フローへ移行します。事例シナリオの銀行もまさにこの問題に遭遇しました。

おわりに

ハイブリッド共存期はマイグレーションの副産物ではなく、それ自体が設計対象のアーキテクチャです。整理すると次のとおりです。

  • **パターン 1(プロトコルブリッジ)**で認証の信頼できる情報源を素早くモダン側へ移し、
  • **パターン 2(プロキシヘッダー注入)**で修正不可のレガシーアプリを無修正で収容し、
  • **パターン 3(orchestration)**は能力とスケジュールのトレードオフとして選択し、
  • **パターン 4(strangler fig)**のガバナンスで侵食の方向性を強制します。

そして、セッション寿命の整合、ログアウトオーケストレーション、ヘッダースプーフィング防御、移行完了基準 — この4つを共存期運用の常時点検項目とすれば、数年の移行期を事故なく、そしてモダナイゼーションを止めずに通過できます。

参考資料