こんにちは!ネットワークセキュリティの世界へようこそ。技術メディア主筆の「デジタル・コンシェルジュ」です。
最近、テレワークの普及やクラウドサービスの利用拡大に伴い、「ゼロトラスト」という言葉を耳にしない日はありませんよね。その中心的な役割を担うのが「認証」です。中でも、異なるサービス間で「君は誰?」という情報を安全にやり取りするSAML 2.0(サムエル)は、現代のインフラエンジニアにとって避けては通れない必須知識となっています。
今日はその中でも、最も基本的でありながら「中身がどうなっているのかイマイチ分かりにくい」と評判の「HTTP-Redirectバインディング」について、郵便配達の仕組みに例えながら、その裏側でパケットがどう動いているのかを優しく紐解いていきましょう。
—
1. SAML 2.0 HTTP-Redirectバインディングって何?
まず、難しそうな名前を分解してみましょう。「バインディング」とは、簡単に言うと「データの運び方」のことです。
SAMLには大きく分けて「HTTP-Redirect」と「HTTP-POST」という2種類の運び方がありますが、今回解説するRedirectバインディングは、主に「これからログインしたいです!」というリクエスト(SAMLRequest)を、皆さんのブラウザのURLにくっつけて運ぶ方式です。
郵便配達に例えると?
- HTTP-POST: 大きな封筒に書類を詰め込み、宅配便でドサッと届けるイメージ。
- HTTP-Redirect: ハガキの裏側にメッセージを書き、それを「転送届」と一緒に次の場所へ送るイメージ。
ハガキ(URL)は書けるスペースが限られているので、情報をギュッと凝縮して、かつ「途中で誰かに書き換えられていないか」を証明する仕組みが必要になります。
—
2. データをギュッと凝縮する「3つのステップ」
ブラウザのURL欄(アドレスバー)には、文字数の制限があります。あまりに長いリクエストを送ろうとすると、エラーになってしまいます。そこで、SAMLリクエストは以下の3つのステップで「コンパクトな暗号のような文字列」に変換されます。
1. Deflate圧縮: もともとのXML形式のデータを、まずはギュッと圧縮してサイズを小さくします。
2. Base64エンコード: 圧縮されたバイナリデータを、URLとして送れる文字(英数字)に変換します。
3. URLエンコード: = や / といったURLで特別な意味を持つ文字を、安全な形式(%xx)に変換します。
この工程を経て、皆さんがよく目にする SAMLRequest=nZFN... という長い呪文のようなURLが出来上がるわけですね。
—
3. 「署名」という名の魔法の印
さて、ここからが本題です。ただデータを送るだけでは、悪い人が途中でURLを書き換えて、偽のログイン要求を送るかもしれません。そこで登場するのが「署名(Signature)」です。
HTTP-Redirectバインディングの面白いところは、「XMLの中身に署名を入れるのではなく、URLのパラメータとして署名をくっつける」という点です。
検証に必要な3つのパラメータ
URLをよく見ると、SAMLRequest 以外にもいくつかの重要な情報が並んでいます。
SAMLRequest: 圧縮・変換されたリクエスト本体。SigAlg: 署名にどのアルゴリズム(数学的な計算方法)を使ったかの宣言。Signature: データの正しさを証明する「印影」。
これらが揃うことで、受け取った側(IdP:認証局)は「あ、これは間違いなく信頼できるサービス(SP)からのリクエストだ!」と確信できるのです。
—
4. 実践!Pythonで見る署名の仕組み
「百聞は一見に如かず」です。実際にプログラムで、どのようにデータを加工して署名を作っているのか、そのエッセンスを覗いてみましょう。
import zlib
import base64
import urllib.parse
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization
# 1. 元となるSAMLリクエスト(本来はもっと長いXMLです)
saml_xml = '<samlp:AuthnRequest ...>...</samlp:AuthnRequest>'
# 2. Deflate圧縮(ヘッダーなしの圧縮を行うのが一般的です)
# zlib.compressの結果から、最初と最後の数バイト(zlibヘッダー/チェックサム)を除くとDeflateになります
compressed_data = zlib.compress(saml_xml.encode('utf-8'))[2:-4]
# 3. Base64エンコード
base64_request = base64.b64encode(compressed_data).decode('utf-8')
# 4. 署名の対象となる「クエリ文字列」を準備
# 重要:SAMLRequest, RelayState, SigAlg の順番で並べる必要があります
sig_alg = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
query_to_sign = f"SAMLRequest={urllib.parse.quote(base64_request)}&SigAlg={urllib.parse.quote(sig_alg)}"
# 5. 秘密鍵で署名を作成(ここでは鍵の読み込みは省略し、イメージを示します)
# signature = private_key.sign(
# query_to_sign.encode('utf-8'),
# padding.PKCS1v15(),
# hashes.SHA256()
# )
# 6. 最終的なURLの組み立て
# final_url = f"https://idp.example.com/sso?{query_to_sign}&Signature={urllib.parse.quote(base64.b64encode(signature))}"
print(f"送信されるリクエストの一部: {base64_request[:30]}...")
このコードで行っているのは、単なる文字列操作ではありません。「データの整合性を保証するための儀式」なのです。
—
5. 現場でのトラブルシューティングのヒント
皆さんが実際に構築や運用をしているとき、「認証エラー」に直面することがあるでしょう。そんなときは、以下のポイントをチェックしてみてください。
- 順番が命: HTTP-Redirectでは、署名を計算するときのパラメータの順番(
SAMLRequest->RelayState->SigAlg)が1文字でも違うと、検証に失敗します。 - エンコードの罠: URLエンコードを2回やってしまったり、逆に忘れていたりしませんか?ブラウザのアドレスバーで見える姿と、プログラムが処理する前の姿を区別しましょう。
- 証明書の期限: 署名を検証するための公開鍵(証明書)が期限切れになっていないか。これは意外とよくある「あるある」です。
—
終わりに:一歩ずつ、確実な理解を
SAML 2.0の仕様書は英語で何百ページもあり、最初は圧倒されてしまうかもしれません。でも、今回お話ししたように「データを小さくして、印鑑を押して、ブラウザという配達員に託す」という基本の流れさえ押さえておけば、パケットキャプチャを見たときの景色がガラリと変わるはずです。
ゼロトラストの扉を開ける鍵は、こうした泥臭いプロトコルの理解から始まります。
「この SAMLRequest の中身、どうなっているんだろう?」と気になったら、ぜひツールを使ってデコードしてみてください。その一歩が、あなたを一流のセキュリティスペシャリストへと導いてくれます。
次は、もっと奥深い「SAMLレスポンスの検証」の世界でお会いしましょう!応援しています。
コメント