SAML メッセージをデコードする方法
- 手元にあるものを貼り付けます。以下のどれでも使えます。
SAMLResponseまたはSAMLRequestの値そのもの(URL エンコードの有無は問いません)SAMLRequest=…(RelayState、SigAlg、Signatureの有無は問いません)- アドレスバーからコピーした Redirect URL 全体
- 隠し
SAMLResponse入力欄を含む、自動送信される HTML フォーム - 生の XML
- デコーダーがバインディングを判定します。プレーンな Base64 なら HTTP-POST、deflate 圧縮されたデータなら HTTP-Redirect です。
- まず要約を読み、その下の整形された XML を確認します。XML はテキストとして表示され、レンダリングされることはありません。
エンコード 方向はその逆です。AuthnRequest を貼り付けると、Redirect 用の値、SAMLRequest= パラメータ、または Destination 属性から組み立てた完全な URL が得られます。サービスプロバイダーをテストする際にリクエストを再送したいときに便利です。
Redirect バインディングと POST バインディング
| HTTP-Redirect | HTTP-POST | |
|---|---|---|
| 送信方法 | GET リクエストのクエリ文字列 | 自動送信される隠しフォームフィールド |
| エンコード | raw DEFLATE、Base64、URL エンコードの順 | Base64 のみ |
| 主なメッセージ | AuthnRequest、LogoutRequest | アサーションを含む Response |
| 署名 | 別個の SigAlg と Signature パラメータ | メッセージ内の XML 署名 |
| 見分け方 | 固定のプレフィックスがなく、Base64 を外すとバイナリ | PHNhbWxwOl(<samlp:)、PHNhbWwycDp(<saml2p:)、PD94bWwg(<?xml ) |
Redirect が圧縮を使うのは、URL の長さに実質的な上限があるからです。このページの例では、464 文字の AuthnRequest が deflate で 273 バイトになり、Base64 の 364 文字として送られます。レスポンスは署名や証明書を含むため URL には大きすぎ、POST で送られます。一部のサービスプロバイダーは Redirect で raw deflate の代わりに zlib ラッパー付きのデータを送りますが、デコーダーはそれも受け付けます。
SSO が失敗したときに確認すること
要約には、ログイン失敗の原因になりやすいフィールドが抜き出されています。
- Status:
urn:oasis:names:tc:SAML:2.0:status:Success以外なら問題です。AuthnFailed、InvalidNameIDPolicy、RequestDeniedなどのサブコードが理由を示し、StatusMessageに詳細が書かれていることもよくあります。 - Destination と ACS URL:サービスプロバイダーに設定したエンドポイントと、
httpsや末尾のスラッシュまで含めて完全に一致する必要があります。 - Audience:SP のエンティティ ID と一致する必要があります。アプリケーション名を変更した後によくある設定ミスです。
- Conditions:
NotBeforeとNotOnOrAfterを手元の時計と照合します。ログインした直後なのに「期限切れ」や「まだ有効ではない」と出る場合は、IdP と SP の時刻のずれが疑われます。 - InResponseTo:
AuthnRequestのIDと一致するはずです。一致しなければ、古いレスポンスか再送されたレスポンスの可能性があります。IdP 起点のログインではこの属性がなく、それを拒否するサービスプロバイダーもあります。 - NameID と Format:SP がユーザーとの照合に使う識別子です。たとえば
emailAddressかpersistentかといった違いがあります。 - 属性:すべての属性とその値を表示します。SP がマッピングするグループやメールアドレスを IdP が送っているかを確認できます。
Python で SAML をデコードする
import base64, zlib
from urllib.parse import unquote
def decode_post(value):
return base64.b64decode(value).decode()
def decode_redirect(value):
raw = base64.b64decode(unquote(value))
return zlib.decompress(raw, -15).decode()
-15 は、ヘッダーのない raw deflate を想定するよう zlib に指示するものです。unquote は URL からコピーした値にだけ使ってください。POST フォームの値はすでにプレーンな Base64 なので、URL デコードすると + が空白に変わることがあります。詳しくは Python での Base64 を、圧縮については raw deflate と zlib のページ を参照してください。
SAML アサーションのプライバシー
アサーションには氏名、メールアドレス、所属グループ、ときにはそれ以上の個人情報が含まれます。有効期限内の正しいアサーションはログイン用トークンとしても使えます。このページはローカルでデコードし、データをどこにも送信できません。それでも、チケットやチャットに貼る前にはアサーションの内容を伏せ、取得したレスポンスは NotOnOrAfter を過ぎるまでセッション Cookie と同じように扱ってください。
よくある質問
ブラウザで SAML レスポンスを取得するには?
開発者ツールを開き、ネットワークタブで Preserve log(ログを保持)を有効にしてからサインインします。サービスプロバイダーの ACS URL への POST リクエストを見つけ、ペイロードから SAMLResponse フォームフィールドをコピーして、ここに貼り付けてください。
SAMLRequest が普通の Base64 デコーダーでデコードできないのはなぜですか?
HTTP-Redirect バインディングでは、XML が Base64 化と URL エンコードの前に raw deflate で圧縮されています。普通のデコーダーではバイナリのノイズしか表示されません。このツールは URL デコード、Base64 デコード、展開を一度に行います。
SAML の署名は検証されますか?
いいえ。メッセージ、アサーション、クエリ文字列が署名されているかどうかは表示しますが、IdP の証明書による署名の検証は行いません。署名の検証はサービスプロバイダー側の役割です。
EncryptedAssertion を復号できますか?
いいえ。暗号化されたアサーションの復号にはサービスプロバイダーの秘密鍵が必要です。デコーダーはその個数を数え、暗号化されていない外側の部分(ステータス、発行者、宛先)は表示します。