Power Automate フローから Logic Apps ロジックアプリへの HTTP 通信をよりセキュアに

# はじめに

たとえば、Power Apps アプリで Azure SQL Database のデータを参照するために、コネクションを作って組織内のユーザーと共有してしまうと、良からぬ者によって良からぬことをやられてしまうかもしれません。[^1]

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/60bafb53-1dcf-bdce-19c6-e09527ee9334.png)

そんな不安は未然に払拭しておいたほうが良いでしょうから、Power Apps アプリや Power Automate フローから Azure SQL Database に対して直接 SQL クエリを実行せずに、目的に応じて実行する SQL クエリを Logic Apps ロジックアプリに隠ぺいして HTTP リクエストで必要な情報を取り込もうと思いました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/4f7379ab-551a-ac15-0632-5088318d28f8.png)

が、この**「HTTP リクエスト」もよりセキュアに通信できないか**と検討してみます。

ゴールは、**「Azure Logic Apps ロジックアプリが特定のクライアント (ここでは Power Automate フロー) からのみ HTTP リクエストを受け付けるようなアクセス制限を設ける」**です。


# Azure Logic Apps 要求エンドポイント

要求エンドポイントでは、以下のいずれかの承認スキームが用意されています。

- Shared Access Signature (SAS)
- Azure Active Directory Open Authentication (Azure AD OAuth)

機能的に片方の承認スキームを有効にしてもう片方の承認スキームを無効にすることはできません。また、リクエストする時に両方のスキームを同時に使用するとエラーが発生してしまいます。


## 承認スキーム：SAS

When a HTTP request is received (HTTP 要求の受信時) トリガーを実装したフローを保存すると要求エンドポイント URL が生成されますが、この URL には [SAS クエリパラメーター](https://docs.microsoft.com/ja-jp/azure/logic-apps/logic-apps-securing-a-logic-app?WT.mc_id=AZ-MVP-5003113&tabs=azure-portal#generate-shared-access-signatures-sas)が含まれています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/875fd692-6d97-19fc-3fd4-2eabc653fd41.png)

ただこの URL を知っていればどのクライアントからでもアクセスできてしまうので、今回のゴールにはちょっと適していないですね。

https://docs.microsoft.com/ja-jp/azure/logic-apps/logic-apps-securing-a-logic-app?WT.mc_id=AZ-MVP-5003113&tabs=azure-portal#generate-shared-access-signatures-sas


## 承認スキーム：Azure AD OAuth

Logic Apps ロジックアプリで承認ポリシーを定義することで、要求エンドポイントへアクセスする際に Azure AD OAuth で承認させることができます。
※承認ポリシーは複数個定義することもできる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/1e6ff50c-efbc-4129-a10b-8f465fb129d4.png)

クライアントからロジックアプリで承認するためのアクセストークンを含めて HTTP リクエストを要求エンドポイントに対して発行すると定義されたいずれかの承認ポリシーに含まれるすべてのクレームが一致した場合、承認が成功します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/ed578e05-4844-538a-2089-387e5ccf0944.png)

今回のゴールにはこちらのほうが適しているようです。

https://docs.microsoft.com/ja-jp/azure/logic-apps/logic-apps-securing-a-logic-app?WT.mc_id=AZ-MVP-5003113&tabs=azure-portal#enable-azure-active-directory-open-authentication-azure-ad-oauth


# 各種設定と実装

ここでは、Logic Apps ロジックアプリの要求エンドポイント呼び出し時に、Azure AD OAuth 承認させるための各種設定と実装を順に説明していきます。


## Azure AD での作業

Azure AD テナントにアプリを登録してサービスプリンシパルを作成します。
※下図のオレンジ色の線で囲った部分

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/af20b5bc-583e-c731-17ff-33be19f48393.png)

ここでは、この操作を Power Automate フローが格納される環境の Microsoft 365 サブスクリプションに紐づく Azure AD テナントで行います。

なお、これ以降では各フローを以下のように呼ぶことにします。

- Logic Apps ロジックアプリ → **受け側**
- Power Automate フロー → **送り側**


### アプリの登録：受け側 (Logic Apps ロジックアプリ) 

まずは「受け側」。

Azure ポータルから Azure AD テナントを開くか、Microsoft 365 管理センターから Azure AD テナントを開きます。そして [管理] セクションから [アプリの登録] を開いて [新規登録] を行います。

「表示名」に適当な名前を入力し、「サポートされているアカウントの種類」から「この組織ディレクトリに含まれるアカウント」を選択します。「リダイレクト URI」は不要なので省略します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/cb6c9969-cf68-d95b-42a2-d455286574df.png)

登録が完了したら、[概要] を開きます。「アプリケーション (クライアント) ID」と「ディレクトリ (テナント) ID」をメモっておきます。後で使います。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/fc5971f8-87a2-da9c-472a-4284829b37e7.png)

次に、「送り側」から「受け側」へアクセスできるようにするために、スコープを介して「受け側」の要求エンドポイントを公開します。
[管理] セクションの [API の公開] を開きます。

「アプリケーション ID の URI」を設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/b4820167-d559-de9a-e1c6-51529c9161b0.png)

「スコープ」を追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/79779c33-0167-6179-2d45-63f6b7b93e2e.png)

Azure AD テナントでの「受け側」の作業は以上となります。


### アプリの登録：送り側 (Power Automate フロー)

続いて「送り側」。

Azure ポータルから Azure AD テナントを開くか、Microsoft 365 管理センターから Azure AD テナントを開きます。そして [管理] セクションから [アプリの登録] を開いて [新規登録] を行います。

「表示名」に適当な名前を入力し、「サポートされているアカウントの種類」から「この組織ディレクトリに含まれるアカウント」を選択します。「リダイレクト URI」は不要なので省略します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/c3b61b7e-928e-ff6b-d91c-2f7402e07fac.png)

登録が完了したら、[概要] を開きます。「アプリケーション (クライアント) ID」と「ディレクトリ (テナント) ID」をメモっておきます。後で使います。
※「ディレクトリ (テナント) ID」は先ほどの「受け側」でメモった値と同じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/f3111c9e-3d04-a1a9-ab13-8190f23ef3e7.png)

次に、アクセストークンを取得する時に「送り側」自身を認証するために必要な「クライアント シークレット」を追加します。
[管理] セクションの [証明書とシークレット] を開きます。

新しい「クライアント シークレット」を作成します。
作った「クライアント シークレット」の値をメモっておきます。後で使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/d636520f-660e-1b54-efbf-25d49b7a3b08.png)

次に「送り側」から「受け側」にアクセスするためのアクセス許可を追加します。
[管理] セクションの [API のアクセス許可] を開きます。

先ほどの「受け側」で追加した「スコープ」を追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/59d341f0-7b5e-1d9c-c619-a6340dfdcfa4.png)

Azure AD テナントでの「送り側」の作業は以上となります。


## 受け側 (Logic Apps ロジックアプリ) での作業

「送り側」からの HTTP リクエストに含まれるアクセストークン内のクレームをチェックするための「Azure AD 承認ポリシー」を定義します。
※下図のオレンジ色の線で囲った部分

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/7cf000b9-fe34-d521-13e6-8b2a3d3f9e13.png)


### フローを作成

まず、外部から HTTP リクエストを受け取るために「When a HTTP request is received (HTTP 要求の受信時)」トリガーを含むフローを作成します。保存すると要求エンドポイントの URL が発行されるので、これをメモっておきます。後で使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/875fd692-6d97-19fc-3fd4-2eabc653fd41.png)


### Azure AD 承認ポリシーを追加

HTTP リクエストに含まれるアクセストークンを検証するためのポリシーを定義します。
[設定] セクションの [承認] を開き、以下のように「ポリシー」を追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/1e6ff50c-efbc-4129-a10b-8f465fb129d4.png)

ここでは、追加するポリシーの内容を以下のように設定しました。

| 追加するクレーム | 名前 | 値 | 説明 |
|:-|:-|:-|:-|
| 標準 | Issuer | `https://sts.windows.net/<ディレクトリ (テナント) ID>/`<br />※末尾の`"/"`を忘れずに | 発行者 (Azure AD テナント) を識別する識別子 |
| 標準 | Audience | 「受け側」のアプリケーション (クライアント) ID | トークンの対象となる受信者のアプリケーション ID |
| カスタム | appid | 「送り側」のアプリケーション (クライアント) ID | トークンを使用するクライアントのアプリケーション ID |

これで、受け取ったアクセストークンのクレームをポリシーで指定するクレームを照合するようになります。

アクセストークンのその他のクレームについては、以下のドキュメントをご参照ください。

https://docs.microsoft.com/ja-jp/azure/active-directory/develop/access-tokens?WT.mc_id=AZ-MVP-5003113#payload-claims


## 送り側 (Power Automate フロー) での作業

HTTP アクションでアクセストークンを含むリクエストを発行するようにフローを実装します。
※下図のオレンジ色の線で囲った部分

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/d8cc4552-a3c9-3d8d-3743-1ccd2840cc80.png)


### HTTP アクション

HTTP アクションでアクセストークンを含むリクエストを発行するように実装します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/ed578e05-4844-538a-2089-387e5ccf0944.png)

ここでは、HTTP アクションのオプションを以下のように設定しました。

| オプション | 入力値 |
|:-|:-|
| Method | 「受け側」の「When a HTTP request is received (HTTP 要求の受信時)」トリガーで指定したメソッドを選択 |
| URI | 「受け側」の要求エンドポイント URL <br />※URL から SAS クエリパラメーター (sp, sv, sig) を取り除くこと <br />※→`https://<request-endpoint-URI>?api-version=2016-10-01` |
| Authentication | 「Active Directory OAuth」を選択 |
| Authority | 空白 (＝デフォルト)<br />※デフォルト＝`https://login.windows.net/` |
| Tenant | ディレクトリ (テナント) ID |
| Audience | 「受け側」のアプリケーション (クライアント) ID |
| Client ID | 「送り側」のアプリケーション (クライアント) ID |
| Credential Type | 「Secret」を選択 |
| Secret | 「送り側」のクライアント シークレット値 ​|


# 動作確認

それでは実行してみて、「送り側」、「受け側」がともに正常に動作しているか確認します。


## 送り側 (Power Automate フロー) を確認

下図のように HTTP アクションが正常に動作していることを確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/780b7402-3c82-df62-606c-c9dc7cc77b57.png)


## 受け側 (Logic Apps ロジックアプリ) を確認

下図のように When a HTTP request is received (HTTP 要求の受信時) トリガーが正常に動作していることを確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/a76ba2af-b2cb-b7c1-4ca9-cca277c96c93.png)


## サインイン ログを確認

Azure AD テナントで、サービスプリンシパルのサインインログでも認証が成功していることが確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/ce1aa6ac-f33d-ba7f-99d9-db38eed024d8.png)


# まとめ

これまでの各種設定・実装・動作確認を踏まえると、一連の承認の流れは下図のようになっていることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/04c4a508-65bb-b8c9-93da-fd21af6fb612.png)

「受け側」の Azure Logic Apps ロジックアプリの「Azure AD 承認ポリシー」を定義することによって、特定のクライアント (「送り側」の Power Automate フロー) からしか HTTP リクエストを受け付けないようにアクセス制限することができました。

Azure Logic Apps のセキュリティ保護については、以下のドキュメントあたりをご覧いただければと思います。

https://docs.microsoft.com/ja-jp/azure/logic-apps/logic-apps-securing-a-logic-app?WT.mc_id=AZ-MVP-5003113&tabs=azure-portal

https://docs.microsoft.com/ja-jp/azure/logic-apps/create-managed-service-identity?WT.mc_id=AZ-MVP-5003113&tabs=consumption

https://docs.microsoft.com/ja-jp/azure/logic-apps/block-connections-connectors?WT.mc_id=AZ-MVP-5003113


[^1]: [Power Apps Guide - SQL - Caution! This is how users can hack shared SQL connections - Power Apps Guide - Blog](http://powerappsguide.com/blog/post/sql-security-how-to-hack-implicit-connections)