SAML
Security Assertion Markup Language ・ さむる
IdPの認証結果を署名付きXMLのアサーションにしてSPへ運ぶ、企業向けSSOの標準。
✦ 30秒で試す — 航海パスワードを預からないアプリが、どうやって本人だと信じるのか業務アプリで署名を検証すると、結果は?予想してみる概要
SAML(Security Assertion Markup Language、読みは「サムル」)は、認証の結果を組織やサービスの境界を越えて受け渡すための、XML ベースの標準です。「この人は、この IdP で、この時刻に、この方法でログインに成功した」という主張(アサーション)を XML 文書で表し、IdP の秘密鍵でデジタル署名します(署名の仕組みは公開鍵暗号です)。その文書をブラウザに持たせてサービス側へ届け、サービス側は署名を確かめてから利用者を受け入れます。現行版の SAML 2.0 は 2005 年に標準化され、以来 20 年にわたってエンタープライズの SSO を支えてきました。
SAML では、ログインを受け付けるサービスを SP(Service Provider)と呼びます。社給アカウントで Salesforce や Workday のような業務 SaaS(クラウドで提供される業務アプリ)にログインするとき、会社の IdP とその SaaS の間を運ばれているのが SAML のアサーションです。後発の OIDC が新規構築の主流になった今でも、企業向け SaaS では SAML 対応がほぼ必須の要件で、「SSO 対応」と書かれた管理画面を開けば、まず SAML の設定項目に出会います。
なぜ生まれたか
2000 年代初頭、企業システムの Web 化が進み、社内ポータル・人事システム・取引先のサービスなど、ログインを求める画面が急増しました。認証は各システムがばらばらに抱えていたので、利用者はパスワードの乱立に苦しみました。より根の深い問題は、組織の境界を越えて「この人は確かに当社の社員として認証済みです」と伝える標準的な方法がなかったことです。各社が独自のトークン形式や連携方式を作ると、接続先が増えるたびに個別の実装が膨れ上がります。
そこで標準化団体 OASIS が、認証・属性・認可判断といった「セキュリティに関する主張」を XML で書き、署名で真正性を保証して受け渡す共通言語として SAML を策定しました。2002 年の SAML 1.0 から始まり、2005 年の SAML 2.0 で、Liberty Alliance の ID-FF や大学間連携の Shibboleth の成果を取り込んで一本化されます。これで「一度この標準に対応すれば、どの組織の IdP とも連携できる」相互運用性が実現しました。
設計には当時の技術観がそのまま表れています。モバイルアプリも SPA もまだなく、Web サービス技術の中心は XML でした。また、IdP と SP は別の組織にあり、互いのサーバが直接通信できるとは限りません。そこで SAML の代表的な使い方は、利用者のブラウザを運び屋にして、IdP と SP が直接通信しなくても成立するように作られました(直接通信を使う方式も用意されていますが、主流ではありません)。
詳細
アサーション: 署名された「主張」の文書
SAML の中心はアサーション(assertion、主張)です。IdP が発行する XML 文書で、3種類の内容を運べます。「誰が・いつ・どの方法で認証されたか」を伝える認証ステートメント、メールアドレスや所属部署などを伝える属性ステートメント、リソースへのアクセス可否を伝える認可決定ステートメントです。実務の SSO で使うのは主に前の2つで、SP はアサーションから「利用者の識別子(NameID)」「認証済みである事実」「必要な属性」を受け取ります。
アサーションは、SP へ送られる応答(SAMLResponse)という封筒に入って届きます。アサーションには IdP の秘密鍵による XML 署名が付き、SP は事前に受け取っておいた IdP の証明書(公開鍵暗号の公開鍵)で検証します。さらに、宛先の SP を限定する Audience、有効期限(NotBefore・NotOnOrAfter)、どの要求への応答かを示す InResponseTo などの条件が埋め込まれ、盗んだアサーションを別のサービスに持ち込んだり、後で使い回したりする攻撃を防ぎます。この「署名付きの主張に、宛先と期限の限定を付ける」構造は、のちの JWT と OIDC の ID トークンにそのまま受け継がれました。
Web ブラウザ SSO のフロー
SAML の代表的な使い方が「Web ブラウザ SSO プロファイル」です。IdP と SP は直接通信せず、認証リクエスト(AuthnRequest)もアサーションも、ブラウザを経由して受け渡されます。このとき使う運び方を「バインディング」と呼びます。
(1)利用者がブラウザで SP の保護されたページを開くと、(2)SP は AuthnRequest を作り、圧縮して Base64 にしたものを URL のクエリに載せて、ブラウザを IdP へリダイレクトさせます(HTTP-Redirect バインディング)。元のページに戻るための RelayState も一緒に載せます。(3)IdP は利用者を認証します。IdP 側にすでにログイン済みのセッションがあれば、この画面は出ません。これが「一度のログインで複数のサービスに入れる」理由です。
(4)認証に成功すると、IdP は署名付きの SAMLResponse を隠しフィールドに埋め込んだ HTML のフォームをブラウザに返します。ページが読み込まれると JavaScript がフォームを自動送信し、SAMLResponse は SP の受け口(ACS: Assertion Consumer Service)へ POST されます(HTTP-POST バインディング)。XML のアサーションは大きく、URL のクエリに載せきれないので POST で運ぶのです。(5)SP は署名・発行者・宛先・期限・InResponseTo(自分が出した要求への応答か)を検証し、通れば自前のセッションを開始します。以降は SAML の出番なくサービスを使えます。
ブラウザを通らない方式もあります。Artifact バインディングでは、ブラウザが運ぶのは短い参照番号(アーティファクト)だけで、SP はそれを使って IdP から SOAP のサーバ間通信でアサーションを受け取ります。ブラウザにアサーションが残らない反面、SP から IdP へ直接つながる経路が要るので、導入は多くありません。導入時には、IdP と SP がエンドポイントの URL と証明書を記した「メタデータ」という XML を交換して信頼関係を設定します。
署名した範囲と、読む範囲: XML Signature Wrapping
上のフローで、SAMLResponse は利用者のブラウザを通ります。つまり、利用者が悪意を持てば、文書を自由に書き換えてから POST できます。それでも安全なのは署名があるからですが、ここに SAML 最大の落とし穴があります。XML 署名は「文書全体」ではなく、Reference で ID を指した特定の要素に掛かります。署名検証は「ID=a1 の要素は改ざんされていない」ことしか保証しません。
もし SP の実装が、署名検証は ID で要素を探して行い、利用者の身元は「文書の中で最初に見つかった Assertion」から読む、という別々のやり方をしていたらどうなるでしょう。攻撃者は、自分の一般社員アカウントで正規にログインして署名済みのレスポンスを手に入れ、そこに署名のない偽のアサーション(NameID=admin)を金の枠の外に差し込みます。署名検証は ID=a1 を見つけて「改ざんなし」と答え、身元の読み取りは先頭の偽物を読む。署名は正しいまま、別人としてログインできてしまいます。これが XML Signature Wrapping(XSW)と呼ばれる攻撃です。
2012 年に Somorovsky らが主要な SAML 実装を調べた研究では、14 の実装のうち 11 がこの種の攻撃で破られました。その後も、XML のコメントの扱いの違いで NameID を切り詰めさせる手口(2018 年)や、2 つの XML パーサーの解釈の食い違いを突く手口(2025 年の ruby-saml)など、同じ根を持つ脆弱性が繰り返し見つかっています。XML は同じ内容を何通りにも書けるので、署名の前に文書を正規化(canonicalization)する必要があり、この複雑さが「署名した範囲」と「読む範囲」をずらす隙を生みます。
下の部品で、署名済みのレスポンスに偽のアサーションを差し込んでみてください。差し込む場所(金の枠の前・中・後)と SP の読み方を変えると、どこで弾かれ、どこで通ってしまうかが分かります。
あなたは一般社員 alice として正規にログインし、IdP が署名したレスポンスを手に入れました。朱の偽アサーション(admin)をつかんで、左の木の点線の隙間に差し込んでください。
対策の核心は「署名検証が確かめた要素を、そのまま読む」ことです。ID で要素を探し直すのではなく、検証ライブラリが返した要素だけを使い、アサーションが複数あるレスポンスやスキーマに合わない文書はその時点で拒否します。自前で XML 署名を検証するのは避け、実績があり更新の続いているライブラリを使うのが鉄則です。
そのほかの落とし穴
IdP のポータルから始める IdP-initiated フローは、SP が「自分が開始した要求への応答か」を InResponseTo で確かめられません。攻撃者が自分のアサーションを被害者のブラウザに POST させる、CSRF に似たログイン強要の余地があるため、可能なら SP-initiated を選ぶのが安全側の設計です。同じ理由で、使用済みのアサーション ID を覚えておいて再利用を拒否すること、サーバ間の時計のずれを数分の許容幅で吸収することも必要です。
運用面では、署名証明書の期限切れが「ある日突然、全社員がログインできなくなる」定番の障害です。IdP のメタデータに新旧の証明書を並べて移行する手順を決め、期限を監視しておきます。また、SAML には複数の SP から一斉にログアウトさせる Single Logout もありますが、各 SP をブラウザで順に巡る仕組みのため、途中で止まりやすく、あてにしすぎないのが現実的です。
OIDC との世代対比
SAML と OIDC は「IdP の認証結果を署名付きで SP に運ぶ」という目的も、リダイレクトの往復という骨格も共通です。違いは主に世代の技術観にあります。SAML は XML とブラウザの POST を基盤とするエンタープライズ Web 世代の設計、OIDC は JSON・REST API・OAuth を基盤とするモバイル・API 世代の設計です。
使い分けは「SP 側が何に対応しているか」でほぼ決まります。歴史の長い業務 SaaS では SAML 対応が最も枯れていて、企業の IdP 管理者にとっては SAML 連携が最短経路です。一方、自社開発のアプリや、モバイル・SPA を含む構成では OIDC が自然な選択です。Okta や Microsoft Entra ID などの IdP は両方に対応しているので、1つの IdP の下に SAML の SP と OIDC のクライアントが混在するのが、今日の典型的な姿です。
試問
あなたは業務アプリ(SP)を守る側です。ログインを代行する IdP は、社員 alice の「アサーション」(本人と権限の主張)に署名して送ってきますが、その文書はブラウザを通るので、署名の範囲(金の枠)の外に、署名のない偽のアサーション(admin)を足されることがあります。SP が本人を読む方法を選び、偽物を枠の前・中・後の隙間に差し込んで(つかむか、タップで順に)、SP が誰としてログインさせるかを確かめます。
あなたは一般社員 alice として正規にログインし、IdP が署名したレスポンスを手に入れました。朱の偽アサーション(admin)をつかんで、左の木の点線の隙間に差し込んでください。
