SAML
WebSphere
SSO

WebSphere Application ServerでSAML SSO(SP-redirected編)

今回はTAI(Trust Association Interceptor)のカスタムプロパティを使って、SP-redirectedのSAML SSOシナリオを実践していきます。

TAIの設定追加

まずは、認証なしでサーブレットにアクセスしてきたときに、IdP-initiated SSOが発動するようにTAIのプロパティを追加します。

WASの管理コンソールから、「セキュリティー」ー「グローバルセキュリティー」ー「トラスト・アソシエーション」に入ります。スクリーンショット 2018-01-27 7.04.04.png
「インターセプター」に入ります。スクリーンショット 2018-01-27 7.04.14.png
TAIのカスタムプロパティに入ります。スクリーンショット 2018-01-27 7.04.31.png
認証エラー時にリダイレクトさせるURLの設定は、"sso_1.sp.login.error.page"です。値にIdP-initiatedのときに使用したURL("https://www.alto.net/openam/idpssoinit?metaAlias=%2Fidp&spEntityID=https%3A%2F%2Fwww.docker.mognet.net%2Fsamlsps%2Facs&binding=urn%3Aoasis%3Anames%3Atc%3ASAML%3A2.0%3Abindings%3AHTTP-POST&NameIDFormat=urn:oasis:names:tc:SAML:2.0:nameid-format:transient&RelayState=https%3A%2F%2Fwww.docker.mognet.net%2Fsnoop")を入れて「OK」します。
スクリーンショット 2018-01-27 7.35.42.png
サーバを再起動します。

SP-redirected SSO確認

Firefoxを立ち上げ、"https://www.docker.mognet.net/snoop"に直接アクセスします。"SAML tracer"でキャプチャしておきます。
スクリーンショット 2018-01-27 7.53.38.pngスクリーンショット 2018-01-27 7.53.44.pngスクリーンショット 2018-01-27 7.53.49.png

はい、無事にIdP-initiatedと同じフローになりました。SAML tracerでキャプチャした内容を見てみます。

snoop(抜粋)
GET https://www.docker.mognet.net/snoop HTTP/1.1
Host: www.docker.mognet.net

HTTP/1.1 302 Found
Location: https://www.alto.net/openam/idpssoinit?metaAlias=%2Fidp&spEntityID=https%3A%2F%2Fwww.docker.mognet.net%2Fsamlsps%2Facs&binding=urn%3Aoasis%3Anames%3Atc%3ASAML%3A2.0%3Abindings%3AHTTP-POST&NameIDFormat=urn:oasis:names:tc:SAML:2.0:nameid-format:transient&RelayState=https%3A%2F%2Fwww.docker.mognet.net%2Fsnoop

レスポンスコード302とプロパティに設定したURLがLocationとして返されていることがわかります。

実験(ACSに直アクセスしてみる)

今度はビジネスアプリケーション(snoop)(笑)ではなく、ACSに直接アクセスしてみます。認証されていないという点では同じなので、同様にIdP-initiated SSOが発動されるはずです。

Firefoxを立ち上げ、"https://www.docker.mognet.net/samlsps/acs"に直接アクセスします。
結果、画面遷移は同じでしたが、トレース内容を見ると興味深い差異がありました。
スクリーンショット 2018-01-27 8.03.26.png
ACSに対して2回SAMLアサーションをPOSTしています。1回目のレスポンスは未認証時のレスポンスと同じ"302 Found"です。SAMLアサーションをTAIが拒否したようです。OpenAMは、ログイン済みユーザがログインページにアクセスしてきたらそのまま次の画面へ自動遷移させるので、この辺の動きはユーザには見えません。

ログ追跡

トラブルシュートを有効にしてトレース情報を追ってみます1
トレースで役立ちそうなキーワードは"ACSTrustAssoc""TAIWrapper"あたりになりそうです。

trace.log(抜粋加工)
>  createTAIErrorResult(req[com.ibm.ws.webcontainer.srt.SRTServletRequest], res[com.ibm.ws.webcontainer.srt.SRTServletResponse], msg[CWSML7033E: The Security Assertion Markup Language (SAML) Web single sign-on TAI is unable to perform a redirect to the requested target URL [https://www.docker.mognet.net/samlsps/acs].], before[true]) Entry
〜省略〜
3   Resolved error page [https://www.alto.net/openam/idpssoinit?metaAlias=%2Fidp&spEntityID=https%3A%2F%2Fwww.docker.mognet.net%2Fsamlsps%2Facs&binding=urn%3Aoasis%3Anames%3Atc%3ASAML%3A2.0%3Abindings%3AHTTP-POST&NameIDFormat=urn:oasis:names:tc:SAML:2.0:nameid-format:transient&RelayState=https%3A%2F%2Fwww.docker.mognet.net%2Fsnoop]
3   Sending redirect
<  createTAIErrorResult Exit
3   SAMLResponse could not be verified. Auto Re-login. 
<  invokeTAIbeforeSSO:null Exit
<  negotiateValidateandEstablishTrust returns [not null] Exit
<  negotiateAndValidateEstablishedTrust(): status code = 403 Exit

あ、ちゃんとエラーコード出てますね。。。30分くらいトレース追いかけてたアホは私です。

SystemOut.log
[1/27/18 8:44:35:154 JST] 000000bf ACSTrustAssoc E   CWSML7033E: The Security Assertion Markup Language (SAML) Web single sign-on TAI is unable to perform a redirect to the requested target URL [https://www.docker.mognet.net/samlsps/acs].
[1/27/18 8:44:35:156 JST] 000000bf ACSTrustAssoc E   CWSML7030E: The redirect target URL, [https://www.docker.mognet.net/samlsps/acs], matches the value for the assertion consumer service (ACS) URL configured for this service provider.  You cannot redirect to the ACS URL.  The ACS URL is configured on the [sso_<id>.sp.acsUrl] TAI custom property.  CWSML7034I: The redirect target URL was retrieved from the WasSamlSpReqUrl cookie on the request.  

SAML tracerによるトレースから以下のことがわかります。

  • 未認証のユーザがACSにアクセスしてきたら、TAIのカスタムプロパティ"sso_1.sp.login.error.page"に指定したURLをレスポンスヘッダLocationにセットして、HTTP 302レスポンスを返す
  • さらにこのときに特殊なCookieを返す(WasSamlSpReqURL
  • このWasSamlSpReqURLの値はオリジナルのリクエストURL(このケースではhttps://www.docker.mognet.net/samlsps/acs
  • IdPで認証してACS URLへアクセスした際にクライアントはWasSamlSpReqURLクッキーをリクエストヘッダに付与する

また、SystemOut.logから以下のことがわかります。

  • リダイレクトターゲットのURLはWasSamlSpReqUrlクッキーの値で決まる
  • リダイレクトターゲットのURLはACSのURLと同じではダメ

1回目のSAMLアサーションのPOSTでエラー(302)になったのはこういうわけだったようです。この時のレスポンスでは当該のクッキーを空にして再度リダイレクトレスポンスを返すので、2回目のPOST時にはWasSamlSpReqUrlクッキーがリクエストヘッダに付与されず、通常のIdP-initiatedのリクエストと同様に処理されたと考えられます。

事象の再整理

SAML tracerのトレースからこの現象を時系列に再整理します。

1. ACSへ直接クライアントから"GET"リクエスト

スクリーンショット 2018-01-27 11.08.32.png

  • レスポンスコード302
  • Locationsso_1.sp.login.error.pageのURLをセット
  • WasSamlSpReqURLクッキーに"GET"リクエストのリクエストURLをセット

2. ユーザ認証したクライアントがACSへ"POST"リクエスト(with SAMLアサーション&WasSamlSpReqURLクッキー

スクリーンショット 2018-01-27 11.17.08.png

  • TAILはリダイレクト先URL(WasSamlSpReqURLの値)が自分自身(ACS)なのでNGと判断しレスポンスコード302
  • 再度Locationsso_1.sp.login.error.pageのURLをセット
  • WasSamlSpReqURLクッキーをクリア

3. 2度目のACSへの"POST"リクエスト(with SAMLアサーション、NO WasSamlSpReqURLCookie)

スクリーンショット 2018-01-27 11.22.02.png

  • まともなクリエストなので認証OKで302レスポンス
  • LocationRelayStateのURLをセット
  • LtpaToken2クッキーを発行

比較のため、snoopへ未認証アクセスした際のトレースも同様に整理しておきます。

1. snoopへ直接クライアントから"GET"リクエスト

スクリーンショット 2018-01-27 11.36.22.png

  • レスポンスコード302
  • Locationsso_1.sp.login.error.pageのURLをセット
  • WasSamlSpReeqURLクッキーに"GET"リクエストのリクエストURLをセット

2. ユーザ認証したクライアントがACSへ"POST"リクエスト(with SAMLアサーション&WasSamlSpReqURLクッキー

スクリーンショット 2018-01-27 11.39.29.png

  • まともなクリエストなので認証OKで302レスポンス
  • LocationRelayStateのURLをセット
  • LtpaToken2クッキーを発行

おもいがけず深い沼にはまったので今回はこれくらいにしておきます。次回こそはJavaの話題に入りたいところです。

参考文献