Azure AD とアプリケーションを SAML 連携して SAML Binding の動作を見てみる。

#はじめに

Azure AD とエンタープライズ アプリケーションを SAML 連携することで、Azure AD ユーザーを利用して SAML 連携したアプリケーションに対してシングル サインオンできるようになります。

SAML 2.0 の SAML Binding とは Assertion (要求) を行う SP 側を SAML Requester 、 Assertion を受け取る IdP 側は SAML Responder とした場合、 SAML メッセージをどのような方法で行うかを定義づけることを言います。

SAML Binding は全部で以下の通り、 6 種類ありますが、現在は HTTP Redirect Binding と HTTP POST Binding が主流となっています。

・SAML SOAP binding (based on SOAP 1.1)
・Reverse SOAP (PAOS) binding
・HTTP Redirect Binding
・HTTP POST Binding
・HTTP Artifact Binding
・SAML URI Binding

詳しくは下記ドキュメントに記載がありますので、興味があれば見てみてください。

-参考情報
Bindings for the OASIS Security Assertion Markup Language (SAML) V2.0 – Errata Composite
URL:https://www.oasis-open.org/committees/download.php/56779/sstc-saml-bindings-errata-2.0-wd-06.pdf

HTTP Redirect Binding は <samlp:AuthnRequest> のような短いメッセージを含める時に適しています。
一方 HTTP POST Binding は例えば IdP 側が署名した SAML Assertion などのような長いメッセージを送るときに使います。
端的に言ってしまうと、 SAML Request の場合は HTTP Rdirect Binding 、SAML Response の場合は HTTP POST Binding を使います。

実際に Azure AD のドキュメントにも下記のとおり記載があります。

-参考情報
シングル サインオンの SAML プロトコル
URL:https://docs.microsoft.com/ja-jp/azure/active-directory/develop/single-sign-on-saml-protocol

>クラウド サービス (サービス プロバイダー) は、HTTP リダイレクト バインディングを使用して、 AuthnRequest (認証要求) 要素を Azure AD (ID プロバイダー) に渡します。 Azure AD は、HTTP POST バインディングを使用して、 Response 要素をクラウド サービスに送信します。

今回は、 SAML 2.0 の Protocol Binding の動きを実際にエンタープライズ アプリケーションの AirWatch (現Warkspace ONE) を SAML 連携しながら確認してみます。

#やってみる

下記チュートリアルを参考にしながら、Airwatch と Azure AD を SAML 連携してみます。

-参考情報
チュートリアル:AirWatch を Azure Active Directory と統合する
URL:https://docs.microsoft.com/ja-jp/azure/active-directory/saas-apps/airwatch-tutorial

AirWatch の 30 日評価版は下記 URL から入手できます。

URL:https://www.air-watch.com/about/news/press-releases/press-releases/2011/09/airwatch-further-strengthens-enterprise-mobile-security-with-saml1/

Azure AD と AirWatch 側それぞれにチュートリアルとおりにシングル サインオンの設定をしたあとに、アクセス パネル (myapps.microsoft.com) より、AirWatch にアクセスしてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/4fd521cd-121b-9ddc-2c01-146f6ff3bf50.png)

AirWatch のアイコンをクリック後にシングル サインオンができずに下記画面に遷移しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/4e457989-e5fe-61fb-0240-867d87febb66.png)

下記メッセージを見てみましょう
AADSTS750032: **SAML protocol response cannot be sent via bindings other than HTTP POST** . Requested binding: urn:oasis:names: tc:SAML:2.0:bindings:**HTTP-Redirect**

エラーメッセージを Bing で検索しても残念ながらなにも情報が出てきませんでした。
しかしながら、エラー内容を見ると、SAML Response の Binding は HTTP POST 以外は対応していません、今は Redirect で来てますよ。と書いてあります。

もう1度 Azure AD の SAML 2.0プロトコルのドキュメントを見てみましょう。

>クラウド サービス (サービス プロバイダー) は、HTTP リダイレクト バインディングを使用して、 AuthnRequest (認証要求) 要素を Azure AD (ID プロバイダー) に渡します。 Azure AD は、**HTTP POST バインディング**を使用して、 Response 要素をクラウド サービスに送信します。

つまり、Azure AD は HTTP POST Binding でないと応答できない、と書いてありますね。
チュートリアルとおりに設定したのに設定の食い違いが発生してエラーが出ている状況です。
チュートリアルの該当する箇所を見てみましょう。

[Request] セクションで、次の手順に従います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/b879f1ce-9504-8b3c-412a-457dde6c1e80.png)
a. [Request Binding Type] として [POST] を選択します。
b. Azure portal の [AirWatch でのシングル サインオンの構成] ダイアログ ページで、 [ログイン URL] の値をコピーし、 [ID プロバイダー シングル サインオン URL] テキスト ボックスに貼り付けます。
c. [NameID Format] として [Email Address] を選択します。
d. [Authentication Request Security](認証要求のセキュリティ) として [None](なし) を選択します。
e. [Save] をクリックします。


チュートリアルで言及しているのは、 Request Binding ですね、 Azure AD が返す Response Binding の設定については言及していません。
そこでエラーが出ている現在の AirWatch 側のシングル サインオンの設定を見てみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/298a2f52-439e-1c33-0947-563901056b8f.png)

お気づきでしょうか？
設定が完全に逆になっています。
つまり、Request  Binding は HTTP POST ではなく、 Redirect が正しく、Azure AD が返す Respose Binding は HTTP Redirect ではなく、 HTTP POST が正しいのです。

なんとチュートリアルあるとおりに設定するとエラーになる、ということが分かりました。
ですが、ここまでの説明を見れば、どのように設定をすればいいか分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/eeca70d1-2088-8a34-2849-6929c0a7599c.png)

そうです、上記設定がただしい、 Binding 設定です。
実際に間違った設定と、正しい設定を行った場合の、 SAML Request の Fiddler を比較してみましょう。

下記が Respose Binding は HTTP Redirect に設定した場合の SAML Request (AuthnRequest)
※bindings:HTTP-Redirect になっています。

```XML

<samlp:AuthnRequest ID="_679b83eb-2221-4614-a07f-c714e4d3a885" Version="2.0" IssueInstant="2019-08-16T15:38:56.533Z" Destination="https://login.microsoftonline.com/ca90ee67-329e-4615-b7a1-b2e158252733/saml2" ForceAuthn="false" IsPassive="false" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" AssertionConsumerServiceURL="https://cn1109.awmdm.com/AirWatch/SAML/AssertionService.ashx?binding=HttpRedirect" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
  <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">AirWatch</saml:Issuer>
  <samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" AllowCreate="true" />
</samlp:AuthnRequest>
```

下記が Respose Binding は HTTP POST に設定した場合の SAML Request (AuthnRequest)
※bindings:HTTP-POST になっています。

```XML
<samlp:AuthnRequest ID="_4acc6a85-07e5-490d-b349-cd202e3b5e36" Version="2.0" IssueInstant="2019-08-16T15:43:38.868Z" Destination="https://login.microsoftonline.com/ca90ee67-329e-4615-b7a1-b2e158252733/saml2" ForceAuthn="false" IsPassive="false" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="https://cn1109.awmdm.com/AirWatch/SAML/AssertionService.ashx?binding=HttpPost" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
  <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">AirWatch</saml:Issuer>
  <samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" AllowCreate="true" />
</samlp:AuthnRequest>
```

正しく Binding を設定した場合は下記のとおり AirWatch (Workspace ONE) にシングル サインオンできました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/ac9275b7-0441-201e-0186-e6694fc40414.png)

#おまけ
今回シングル サインオンに成功した SAML Request と SAML Response の Fiddler を取得しましたので公開します。

SAML Request

```XML
<samlp:AuthnRequest ID="_4acc6a85-07e5-490d-b349-cd202e3b5e36" Version="2.0" IssueInstant="2019-08-16T15:43:38.868Z" Destination="https://login.microsoftonline.com/ca90ee67-329e-4615-b7a1-b2e158252733/saml2" ForceAuthn="false" IsPassive="false" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="https://cn1109.awmdm.com/AirWatch/SAML/AssertionService.ashx?binding=HttpPost" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
  <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">AirWatch</saml:Issuer>
  <samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" AllowCreate="true" />
</samlp:AuthnRequest>
```

SAML Response

Fiddler でも下記のとおり HTTP POST と表記されていますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/f24c54a0-63b1-da9c-e7ac-fad19e6908f6.png)

```XML
<samlp:Response ID="_9e803326-93eb-42a1-9a17-1dce36f80a28" Version="2.0" IssueInstant="2019-08-16T15:43:39.324Z" Destination="https://cn1109.awmdm.com/AirWatch/SAML/AssertionService.ashx?binding=HttpPost" InResponseTo="_4acc6a85-07e5-490d-b349-cd202e3b5e36" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
  <Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">https://sts.windows.net/ca90ee67-329e-4615-b7a1-b2e158252733/</Issuer>
  <samlp:Status>
    <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
  </samlp:Status>
  <Assertion ID="_36418bbf-d56a-49af-9c36-7636daa05600" IssueInstant="2019-08-16T15:43:39.324Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
    <Issuer>https://sts.windows.net/ca90ee67-329e-4615-b7a1-b2e158252733/</Issuer>
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
      <SignedInfo>
        <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
        <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
        <Reference URI="#_36418bbf-d56a-49af-9c36-7636daa05600">
          <Transforms>
            <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
            <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
          </Transforms>
          <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
          <DigestValue>ywyIBhjthSa5WE8jZWYbrrUDiIkBpt9hs6rOqixX0Vw=</DigestValue>
        </Reference>
      </SignedInfo>
      <SignatureValue>Qa7yaV94uaWB8ImTH0KqPWyFDDb33bnUEiEPXH07guwVgxhK1To1F9ipgzNUHpkPei/WHc1LEgcj9e9Ycb2bgOTN77YnbldzKXM1SekVguEV/gsTjfJQL7xWpZvowi9r9E/2Gik24o8m1IxzYOYXHGQhpN/Qt6QTq/yx7PloCL+HLJ0BBHl3kPafDtpCYvyvZotRS5t+JrdeqzjfDLN0DT+BupNmCZMr55Bz575whpzPn58uhw2KOxYFV/TjPDw2QciI1LNqvJ02VHFbkMwMKQ49nTTpXxe+B1SGET+iHJJ8TyCXVbVUC3LXMgk/Mo53xfXkK4iP4G5T41jO5x+7Zw==</SignatureValue>
      <KeyInfo>
        <X509Data>
          <X509Certificate>MIIC8DCCAdigAwIBAgIQGkCPLLSyf65KbiYXgJVFdjANBgkqhkiG9w0BAQsFADA0MTIwMAYDVQQDEylNaWNyb3NvZnQgQXp1cmUgRmVkZXJhdGVkIFNTTyBDZXJ0aWZpY2F0ZTAeFw0xOTA4MTUxMjM0NTNaFw0yMjA4MTUxMjM0NTNaMDQxMjAwBgNVBAMTKU1pY3Jvc29mdCBBenVyZSBGZWRlcmF0ZWQgU1NPIENlcnRpZmljYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiXHZdwwrsj9ZpvUFZjAUiBYpxvXZsfMJVr7PhK3b/ICOmBQrRfunE3rphjTsMFmOgGJY4IyOkpUOd4a9JNJjVujpvHsh932tZejx83daPDGT+CJ5GXOsqsFandeklmpzwUr9ezbpjC9/55A+5v6A+5gQnllhgHCqP14G95bGqi6FGlWjVT6Ssb49TY+UmLUGfItyPgvM3A4oYtEenGcKnqJN5mSwDjSA8FTumb2eZ1fEuAnNMi6z2htA9Eg7cWrB1jbDFTOjBxsKGI4PQ5ikqjqG7BNWxAKCwBi/sziivT0+Pts2ju9uHqZVC1e/a57WaA/0toVQNeVbW0TjslpQPwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBqVQGP7BjsxDirn9vA9pQutnyhm5IXxuH8lYmUPhmkfli+aFqX+DyAdYeEj1PxUbDJsM9raaUxypCSQcSCBjRUlPYA0Lo2RTLLE2gTdX6+gW4WB2n6GF0a9Y72eN72gtlxDCC8SSeU4f69oex8aIgxcZmYgAxB0qfBzYVKlmeNLRAzD9OlCCtiKCSMpNXKnDI31CKzPoAID/fS6kKFYDa2i0bEnjGvpBIyUmkcPUm2j4gSxrTr3th+Z98x17LZQMpVBlY3+YTGQ3ARRXgVtyD1KF6izGIaWAwxrVi1Ztt2GFd4aFswHu6AyEKGD9/Pg8HfxN5i2VBktyvcpBSz8B5d</X509Certificate>
        </X509Data>
      </KeyInfo>
    </Signature>
    <Subject>
      <NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">test001@shyamag015.onmicrosoft.com</NameID>
      <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
        <SubjectConfirmationData InResponseTo="_4acc6a85-07e5-490d-b349-cd202e3b5e36" NotOnOrAfter="2019-08-16T15:48:39.324Z" Recipient="https://cn1109.awmdm.com/AirWatch/SAML/AssertionService.ashx?binding=HttpPost" />
      </SubjectConfirmation>
    </Subject>
    <Conditions NotBefore="2019-08-16T15:38:39.308Z" NotOnOrAfter="2019-08-16T16:43:39.308Z">
      <AudienceRestriction>
        <Audience>AirWatch</Audience>
      </AudienceRestriction>
    </Conditions>
    <AttributeStatement>
      <Attribute Name="http://schemas.microsoft.com/identity/claims/tenantid">
        <AttributeValue>ca90ee67-329e-4615-b7a1-b2e158252733</AttributeValue>
      </Attribute>
      <Attribute Name="http://schemas.microsoft.com/identity/claims/objectidentifier">
        <AttributeValue>46cebeac-9e50-48bd-afe5-7d4a21451f6e</AttributeValue>
      </Attribute>
      <Attribute Name="http://schemas.microsoft.com/identity/claims/displayname">
        <AttributeValue>test001@shyamag015.onmicrosoft.com</AttributeValue>
      </Attribute>
      <Attribute Name="http://schemas.microsoft.com/identity/claims/identityprovider">
        <AttributeValue>https://sts.windows.net/ca90ee67-329e-4615-b7a1-b2e158252733/</AttributeValue>
      </Attribute>
      <Attribute Name="http://schemas.microsoft.com/claims/authnmethodsreferences">
        <AttributeValue>http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/password</AttributeValue>
      </Attribute>
      <Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress">
        <AttributeValue>test001@shyamag015.onmicrosoft.com</AttributeValue>
      </Attribute>
      <Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name">
        <AttributeValue>test001@shyamag015.onmicrosoft.com</AttributeValue>
      </Attribute>
      <Attribute Name="UID">
        <AttributeValue>test001@shyamag015.onmicrosoft.com</AttributeValue>
      </Attribute>
    </AttributeStatement>
    <AuthnStatement AuthnInstant="2019-08-16T15:07:12.805Z" SessionIndex="_36418bbf-d56a-49af-9c36-7636daa05600">
      <AuthnContext>
        <AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef>
      </AuthnContext>
    </AuthnStatement>
  </Assertion>
</samlp:Response>
```
#まとめ
私が経験したことのあるエンタープライズ アプリケーションでは、アプリケーション側で SAML Binding の設定を任意に変えられるアプリケーションに出会ったことがありませんでした。
出会ったことがなかったので、Binding の設定まで特に意識することなくシングル サインオンの設定ができていました。
今回は、チュートリアルの内容が誤っていたがために、エラーになり、結果として SAML Binding の設定をSAML Requester および SAML Responder が定義した設定のとおりに行わないと動作しないということを確認できました。

Azure AD の動作仕様として、 SP 側が要求する SAML Request は HTTP Redirect でうけて、 SAML Response は HTTP POST で返すという動作になります。
今回ご紹介したようなエラーに出くわした場合は、SAML Binding の設定を見直すことで事象が解決すると思います。

今回の記事が少しでも参考になれば幸いです。