Linkers Tech Blog

リンカーズ株式会社の開発者ブログです。

もうSAML導入で迷わない!RubySAML設定項目を徹底解説【IdP編】

はじめに

リンカーズ株式会社エンジニアの鈴木です。この記事では RubySAML Gem を利用したSAML SSO認証機能について解説していきます。

SAMLは巨大な仕様であるため、解説すべき事項もかなり多いものとなります。このブログでは解説事項を「IdP関係」「SP関係」「セキュリティ関係」の3種に分け(「IdP」「SP」が何なのかについては後述)、この記事では主に「IdP」に関する項目を解説します。

SAMLとは

SAML (Security Assertion Markup Language) とは、異なるドメイン間で認証・認可に関するデータを交換するためのプロトコルです。ユーザの認証情報や属性、ユーザへの権限の認可といったセキュリティ情報がXML形式で送受信されます。

SAMLを利用することでシングルサインオン (SSO) ならびにシングルログアウト (SLO) が実現できます。詳しくは以下の記事などを参照ください。

SAMLを理解する上で重要な概念である Identity Provider (IdP), Service Provider (SP), Name ID および SAML Metadata を次で説明します。

Identity Provider (IdP)

利用者に対して認証情報を提供するシステムです。具体例としては以下のサービスが知られています。

Service Provider (SP)

IdPが提供する認証情報を利用して、エンドユーザーに機能を提供するシステムです。

Name ID

IdP - SP間共通で認証対象のユーザーを一意に特定する情報を Name ID といい、SAMLが扱う情報の中で最も重要なものです。SAMLとはこのName IDをセキュアにやり取りすることが最大の目的であると言えます。Name IDという名前は当ブログでもしばしば登場します。

Metadata

SPまたはIdPの情報を記述したXMLファイルです。SPにはSP記述用の、IdPにはIdP記述用の仕様が存在します。内容としては以下の情報が主に含まれます。

  • エンティティID
    • ドメイン名やハッシュ値などを結合し、アプリケーションごとに一意になるよう定められた文字列
  • サーバー証明書
  • エンドポイントURI
    • 各アプリケーションが定義している、SAML関係のリクエストを受け付けるURL

多くのIdPがMetadataの公開URLまたはMetadata XMLファイルのダウンロード機能を提供しています。自分のシステムを改修してSAML SSOに対応させる場合、そのシステムにはIdP Metadata (URLまたはファイル) の読み込み機能も提供した方がエンドユーザーにとって便利です。

RubySAML Gemについて

RubySAMLは、SAML認証を実装しようとするクライアント (SP) のためのライブラリで、IdPとのリクエストに用いるXMLの構築・パース機能やサーバー証明書の認証機能を提供しています。

GitHub - SAML-Toolkits/ruby-saml: SAML SSO for Ruby

OneLogin::RubySaml::Settings クラス

運用中のプロダクトにRubySAMLを導入してSAMLを実現する場合、設定項目を正しく決める必要があります。SAML設定項目は OneLogin::RubySaml::Settings クラスにまとめられています。

このシリーズでは OneLogin::RubySaml::Settings の各項目の解説と、このGemを利用する上で設定値をどう決めるべきか解説します。

ソースコード のコメントで言及されている通り、SAML設定項目は大きく分けて「IdP関係」「SP関係」「セキュリティ」の3種類に分類されます。この記事では IdP関係 の設定項目について説明します。SP関係・セキュリティ関係はまた記事を改めて紹介する予定です。

IdP関係オプションの解説

プロダクトが接続する対象のIdPに関する設定を保存します。外部のサービスに関するパラメータであるため、システムとして提供する際はDBに設定値保存用のテーブルを用意した上で、設定値編集用のUIも提供すべきでしょう。

idp_entity_id

IdPが自身を一意に特定するためのIDです。IdPが公開しているため、それを設定します。

idp_sso_service_url

シングルサインオンのリクエストを受け付けるURLです。これもIdPが公開しています。

idp_sso_service_binding

シングルサインオンのリクエストを実行する際の通信プロトコルです。以下の文字列のうちどちらかを設定します。

  • urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST (以下、単に POST)
  • urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect (以下 Redirect)

Ruby on Railsアプリケーションの場合、この値は Redirect に固定 することで充分運用できます。理由はのちほど詳しく解説します。

idp_slo_service_url

シングルログアウトのリクエストを受け付けるURLです。IdPが公開しているURLを設定します。

idp_slo_service_binding

シングルログアウトのリクエストを実行する際の通信プロトコルです。 idp_sso_service_binding と同じ選択肢 (POST, Redirect) になります。

idp_slo_response_service_url

シングルログアウト機能に関するURLです。これもIdPが公開しているURLを設定します。

idp_slo_service_url との違いは、あとで詳しく解説します。

idp_cert

X.509公開鍵暗号体系 におけるIdPのサーバー証明書 (CRT) です。各種URLと同様、IdPが公開しています。

記述方式は PEM形式 の文字列なので、XMLファイルに直接埋め込むことができます。

idp_cert_fingerprint

IdPサーバー証明書の フィンガープリント です。

idp_cert_fingerprint_algorithm

フィンガープリント計算に使われたアルゴリズムです。SHA1, SHA256, ... などが設定されます。

idp_cert_multi

IdPサーバー証明書を複数保存するための設定です。これに対応することで、有効期限が近づいたサーバー証明書の更新の際、新・旧両者の証明書を登録することでダウンタイムなく更新を実施することが可能です。SAML設定機能を提供するのであれば、IdPサーバー証明書の複数登録には対応しておくことを強くお勧めします。

中身は以下の構造のHashです。キーである :signing:encryption は固定で、Valueとして証明書のPEM形式文字列のArrayが入ります。

settings.idp_cert_multi
# => { :signing => ["MII...", "MII..."],
#      :encryption => ["MII...", "MII..."] }

idp_cert (および idp_cert_fingerprint, idp_cert_fingerprint_algorithm) と idp_cert_multi のどちらを利用すべきか、また :signing:encryption の詳細については後述します。

idp_attribute_names

IdPが扱うユーザー情報の属性のうち、対応している属性情報の一覧をmetadataに含めることができ、これを扱うための項目です。

これに関しては設定によりRubySAMLの挙動に影響することはないので、運用上は無視しても問題ありません。

idp_name_qualifier

Name ID に追加的に意味を持たせたい場合に設定されますが、基本的に無くても利用できます。 公式仕様書 にも「IDの用途と意味を明確に決めていない場合は省略すべき」と言及されています。

The NameQualifier and SPNameQualifier attributes SHOULD be omitted unless the identifier's type definition explicitly defines their use and semantics.

valid_until

IdP metadata自体の有効期限がmetadataに含まれているので、これを読み取るための項目です。SAMLの挙動には影響しないため、無視しても問題ありません。

自前でmetadataの読み込み機能を提供する場合、この valid_until と現在時刻を比較し、現在時刻の方が超えていたら読み込み処理をエラーにするという用途に使える可能性があります。

補足

Bindingの選択: POST or Redirect ?

Bindingは Redirect で充分であるとした理由について、次の2つの判断基準を説明します。

  1. IdPが対応していること
  2. Railsとしての実装上の都合

IdPが対応していること

まず、この値はIdPから idp_sso_service_url と一体的に提供されます。具体的にはIdP Metadataに以下の通り記載されており、 Location 属性が idp_sso_service_url, Binding 属性が idp_sso_service_binding に設定すべき値となります。

<SingleSignOnService
    Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
    Location="https://login.microsoftonline.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/saml2"
/>
<SingleSignOnService
    Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
    Location="https://login.microsoftonline.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/saml2"
/>

IdPによってはどちらかのBindingしか対応していなかったり、またBindingによってLocationが異なるケースも存在するようです。いずれにせよ、IdP Metadataを確認すれば idp_sso_service_url に対応する idp_sso_service_binding は読み取れます。

上記の場合、このIdPは同じURL (Location) で POST, Redirect どちらのBindingも対応していることになります。

Railsとしての実装上の都合

Railsアプリに関しては、多くの場合 Redirect の方が圧倒的に都合がいいはずです。

GitHubで公開されている ruby-saml-example を例に取って説明すると、ユーザーからの操作を受け取って上記 idp_sso_service_url を利用する部分は以下の実装になっています。なお、この sso アクションは GET メソッドです。

  def sso
    settings = Account.get_saml_settings(get_url_base)
    if settings.nil?
      render :action => :no_settings
      return
    end

    request = OneLogin::RubySaml::Authrequest.new
    redirect_to(request.create(settings))

  end

https://github.com/SAML-Toolkits/ruby-saml-example/blob/2c1d3e4fc157ba4e0643784a245e7c25754c4dfc/app/controllers/saml_controller.rb#L8-L18

ここで分かる通り, Controllerの実装中に OneLogin::RubySaml::Authrequest#create の実行結果 (idp_sso_service_url にパラメータを追加したString) に対して redirect_to しています。もしここでPOSTリクエストを送信するのであれば idp_sso_service_bindingPOST であるべきでしょう。

ただ、Railsに関してはController内から外部のWebサイトにPOSTリクエストを送信する実装は redirect_to に比べてコストが高くなります。このため、Railsアプリの実装では redirect_to を使い、またこの事情のために idp_sso_service_bindingRedirect を使う方針が便利でしょう。

idp_slo_service_urlidp_slo_response_service_url の違い

シングルログアウト (SLO) とは、同一のIdPを使って複数のSPにログインしている場合に、1回の操作で全てのSPからログアウトする機能です。 idp_slo_service_urlidp_slo_response_service_url はどちらもこのSLOに関して使われるURLで、このSLOの説明を通じて両者の違いを解説します。

SLOの処理フローについて、以下の図を用いて解説します。

saml-slo

出典: https://www.identityserver.com/articles/the-challenge-of-building-saml-single-logout

この図のケースでは、ユーザーは単一のIdPを使って3つのサービス「SP 1」「SP 2」「SP 3」にログインしています。ここで「SP 1」上でシングルログアウトを実行すると、ログイン中の「SP 2」および「SP 3」からもログアウトされるという機能です。

最初に起点となる、SP (上記例での「SP 1」) からIdPへシングルログアウトをリクエストするURLが idp_slo_service_url です。そしてこの機能を実現するために、他のSP (上記例での「SP 2」「SP 3」) はIdPからログアウト要求を受け付け、これに応答してIdPにリクエストを送り返す処理フローが存在します。この時のIdPに送り返すURLが idp_slo_response_service_url です。

シングルログアウトの処理フローと、各種URLとの関係をまとめると以下の通りです。

  1. ユーザーがSP 1のシングルログアウトを起動
  2. SP 1はIdPに対してシングルログアウトリクエストを送信 → idp_slo_service_url
  3. IdPはSP 2, SP 3に対してログアウトリクエストを送信 → single_logout_service_url (SP設定項目。別の記事で解説)
  4. SP 2, SP 3はログアウト処理を実行し、IdPにリクエストを送信 → idp_slo_response_service_url

証明書の保存先: idp_cert or idp_cert_multi

RubySAMLでは、同じIdPサーバー証明書を表現するために idp_certidp_cert_multi 2つの項目を定義しています。これについては、基本的に idp_cert_multi のみ を利用していれば問題はありません。

idp_cert_multi が設定された場合は idp_cert より優先されて利用されます (README より) 。またIdP metadataの読み込み処理において、サーバーが提供する証明書が1個だけでも idp_cert_multi は必ず設定されます (PR #611) 。このため idp_cert_multi だけ設定しておけばSAML SSOの運用が可能です。

Fingerprint は無くていいのか

ただし、 idp_cert には対応して idp_cert_fingerprintidp_cert_fingerprint_algorithm が定義されている一方、 idp_cert_multi にはFingerprintを保存する機能がありません。この点については、以下の理由から運用上の問題はありません。

まず前提として、このIdPサーバー証明書はIdPから送信されたレスポンスをSP側で検証するために利用されます。そしてIdPサーバーからのレスポンスには 証明書そのもの (<X509Certificate> タグ) が必ず含まれます。

RubySAMLは「事前に登録したIdPサーバー証明書」と「レスポンスに含まれるIdPサーバー証明書」を比較することでレスポンスの妥当性を保証します。この比較処理で idp_cert (fingerprintあり) と idp_cert_multi (fingerprintなし) で異なる処理を実行しています。

  • fingerprintあり: レスポンスに含まれるIdP証明書と idp_cert_fingerprint_algorithm からfingerprintを計算し、これと idp_cert_fingerprint を比較
  • fingerprintなし: レスポンスに含まれるIdP証明書の to_pemidp_cert_multito_pem を比較

(出典: ruby-saml/lib/xml_security.rb at master · SAML-Toolkits/ruby-saml)

つまり「fingerprintがあればそれを比較し、無ければPEM形式文字列を比較する」というコンセプトを取っています。このため、証明書によるサーバー認証処理ではfingerprintは必須とされていません。

証明書の :signing:encryption について

先述の通り、 idp_cert_multi:signing:encryption をキーとするハッシュを設定します。それぞれに設定すべき値の方針について、結論から述べると :signing のみ 設定し、 :enctyption については無視 して構いません。

:signing は実際にIdPからのリクエストの検証に参照されるため設定が必須です。一方 :encryption 証明書はRubySAMLのどの処理からも参照されず、システムの挙動には影響しないためです。

:encryption が存在する理由

:encryption という名前から、この証明書はSAMLにおける暗号化機能 (次で解説) で使われていそうな感じですが、実際には使われいません。

では、なぜ :encryption 設定が存在するかというと、これはSAML Metadataの証明書関係の仕様、および暗号化機能の性質が影響していると考えられます。

SAML Metadataには証明書関係の仕様も含まれます。SP MetadataとIdP Metadataはそれぞれ独立した仕様ではありますが、記述がSPとIdPで対称的になっている部分もあります。

SPの証明書は署名と暗号化の両方に使われるため、SP Metadataについても署名 (sign 属性) と暗号化 (encrypt 属性) それぞれの機能に対応した仕様が定義されています。

The providers MAY document the key(s) used to sign requests, responses, and assertions with <md:KeyDescriptor> elements with a use attribute of sign. When encrypting SAML elements, <md:KeyDescriptor> elements with a use attribute of encrypt MAY be used to document supported encryption algorithms and settings, and public keys used to receive bulk encryption keys.

https://docs.oasis-open.org/security/saml/v2.0/saml-profiles-2.0-os.pdf

そして、これと同じことがIdP Metadataの仕様書にも記載されています。つまりIdP metadataの証明書セクション (<md:KeyDescriptor> タグ) にも signencrypt どちらの属性も記述 "できる" ことになっています。

ただし、後述の通りIdP Metadataが利用される場面では セキュアな情報を扱っていない ため、暗号化機能が提供されていません。このためIdPの encrypt 証明書は "定義できるが実質的には使われない" という状態です。

SAMLにおける暗号化とは

SAMLには暗号化機能が定義されており、これを有効にすると XMLENC という仕様に基づいてIdPからSPへのレスポンスXMLが暗号化されます (参考: Microsoftによる解説) 。これを利用することで、レスポンスに含まれる 認証済ユーザーの Name ID というSAMLにおける最もセキュアな情報を暗号化により保護することができます。

RubySAMLも暗号化機能に対応しています (RubySAML: Decrypting IdP SAML assertions) 。利用方法を簡単に説明します。

事前準備

  • SPに復号用の秘密鍵を登録
  • SPとIdP両者に、秘密鍵に対応する公開鍵が含まれる証明書を登録
  • IdPに「このSPへのレスポンスを暗号化する」フラグを設定する

認証実行時

  1. SPからIdP: 認証リクエストを送信
  2. IdP: 登録した証明書に含まれる公開鍵でレスポンスを暗号化
  3. IdPからSP: 暗号化したレスポンスを送信
  4. SP: 登録した秘密鍵でレスポンスを復号

これを踏まえると、反対方向の通信 - つまり「SPからIdPへの通信を暗号化したい」場合は、SP側にIdPの提供した「暗号化用IdP証明書」を設定する必要が生じます。

  • IdPに復号用の秘密鍵を登録
  • SPとIdP両者に、秘密鍵に対応する公開鍵が含まれる証明書 (「暗号化用IdP証明書」) を登録
  • 効果: SPからIdPへのリクエストが暗号化できる

しかし、この機能は提供されていません。理由としては 情報の重要度が異なるため で、SPからIdPへのリクエストにはName IDに匹敵するセキュアな情報は含まれません。

結論として、このケースに対応する暗号化機能がそもそも提供されておらず、したがって「暗号化用IdP証明書」つまり idp_cert_multi:encryption に値の設定は不要であるとの結論が得られます。

⁠おわりに

以上、 OneLogin::RubySaml::Settings の項目のうち、IdPに関する項目の解説と、おすすめする設定値の見解でした。

冒頭で述べた通り、この記事では設定項目の1/3ほどしか解説できていません。「SP編」「セキュリティ編」の記事も計画中なので、しばしお待ちを…。