[OutSystems]Reactive Web AppのServer Action呼び出しのセキュリティ

Reactive Web Appでは処理の比重がクライアント（ブラウザ）側に移っています。
処理は基本的にブラウザ内で（JavaScriptに変換されて）動作するClient Actionで行い、サーバーでの処理が必要なときにServer Action等を呼ぶ形。

ちょっと気になるのがセキュリティです。
画面にはRoleのチェックボックスがあり、権限がない画面にはアクセスできません。
Server Actionにはチェックボックスがありません。Client Actionから呼び出すときに明示的にRoleのAction (Check<Role名>Role Action）を呼ばなければいけないのか？　という疑問がありますね。

実際はOutSystemsが自動的にこの点に対処してくれます。今回はその機能を確認していきます。

# 調査環境
Personal Environment(Version 11.10.0 (Build 22910))
Service Studio(Version 11.10.12)

# Client ActionからのServer Action呼出しの実体
Client ActionからServer Actionの呼び出しがあると、暗黙的にREST APIが自動生成されます。
実行時には、背後でこのAPIを呼び出しているわけです。
ブラウザの開発者ツールを使って通信をキャプチャすると、以下のようなパスです。
明確な法則がありそうですね。

https://<ホスト>/<モジュール名>/screenservices/\<モジュール名\>/\<UI Flow名\>/\<Screen名\>/\<Server Action名\>

また、Server Action呼び出しのパスがScreenの下にあるので、Screenのコンテキスト（Roleチェック）を反映した呼び出しになっているのではないかという推測ができます。

## Input Parameterがある場合
これは余談ですが、Server ActionにInput Parameterがある場合、リクエストボディにInputParametersというキーに対する値として送信されるようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/2bfbb3d5-fcc0-5808-5a65-67f121a74a0f.png)

# 調査方法
Roleをチェックし、Screen ActionからServer Actionを呼び出す画面をいくつか用意します。
Fiddlerで通信をキャプチャしながら画面を操作すると、Server Action呼び出しが記録されます。

Composerという機能を使うと、記録したHTTPSリクエストに修正を加えて任意のリクエストを発行することができるのでこれを利用。
記録された通信を右クリック > Replay > Reissue from Composerと選ぶと、そのリクエストを再発行するための情報がComposerに転記されます。Composer内でリクエストを編集して実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/0e90acda-bdfe-7e41-027a-2b54c336276f.png)

認証状態はCookieで保持しているため、リクエストをコピーするもととなる通信は、テストしたいユーザーでログインした状態で行っている必要があります。

# Screen内で実際に呼んでいないServer ActionはURLを変更しても実行できない
Anonymousにチェックをつけた画面。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/f98b3310-0bf3-69c1-a253-0a9ad5066b20.png)

この画面から、「ServerActionSecurity_ABC」という名前のServer Actionを呼んでいる。
このとき、Fiddlerを使って、URL指定で画面内で利用していないServer Action「ServerActionSecurity_Dummy」を呼んで見る。

リクエストしたURL：
https://\<ホスト\>/\<モジュール名\>/screenservices/\<モジュール名\>/\<UI Flow名\>/ServerActionSecurity_A/ServerActionSecurity_Dummy

結果：
HTTPステータス404
つまり、画面で実際に呼んでいないServer Actionは、URLを変更しても画面の下のURLでは実行できない。

# Registered Roleを要求するScreenから呼んでいるServer Actionを実行するには、ログインが必要
Registeredにチェックをつけた画面。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/94d88905-0a07-a82f-89ee-b1640138abbd.png)

この画面から、「ServerActionSecurity_BC」という名前のServer Actionを呼んでいる。
まず、ログインした状態でこのActionを実行させ、FiddlerのComposerにHTTPリクエストをコピー（手順は。「調査方法」の項目を参照）。
認証状態はクッキーで持っているため、HTTPリクエストのヘッダーから「Cookie: 」の行を削除して「Execute」ボタンをクリックして実行。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/0cc6da17-da1f-eaf0-4d1a-2c155e63d134.png)

結果はHTTPステータスが200で、レスポンスボディが以下のようになります。よって、Server Actionを直接URL指定で読んだ場合、画面がRegisteredを要求していれば、Server ActionもRegisteredを要求してくれる事がわかりました。

``` js
{"versionInfo":{"hasModuleVersionChanged":false,"hasApiVersionChanged":false},"data":{},"exception":{"name":"NotRegisteredException","specificType":"Housesoft_Reactive.NotReactiveTestRole","message":"ReactiveTestRole role required"},"rolesInfo":","}
```

# Roleを要求する画面で利用するServer Actionも同じRoleを要求する
ここでは、まずモジュール内に以下の2つのRoleを用意
- Role1
- Role2

また、それぞれのRoleを持つユーザーを用意
- Role1User：Role1を保持
- Role2User：Role2を保持

以下の2画面を用意し、2ユーザーの権限でリクエストを行ったらどうなるかを確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/936d67be-ec45-e177-0909-60ec07ba870a.png)

Role1Userで「ServerActionSecurity_C」の画面からServer Action「ServerActionSecurity_C」にアクセス。そのリクエストをコピーして「ServerActionSecurity_D」画面のServer Action「ServerActionSecurity_D」を呼ぶようにURLを変更してみたところ、レスポンスは以下のようになりました。

``` js
{"versionInfo":{"hasModuleVersionChanged":false,"hasApiVersionChanged":true},"data":{}}
```

特にエラーにはならないが、本来あるはずのOutput Parameterが返ってきていない。また、デバッガのブレークポイントも止まらないようでした。

ただ、この部分はリクエストの他の部分のチェックをOutSystemsでしているのかもしれません。
本当であれば、RoleのExceptionが返ってくるのを予想していたのですが。
このあたりは後で、再チェックしてなにかわかったら追記します。

# 関連ドキュメント
調査の途中で関連ドキュメントを見つけました。
2021/03/02時点ではまだ翻訳されていませんが、[Reactive web security best practices](https://success.outsystems.com/Documentation/Best_Practices/Security/Reactive_web_security_best_practices)に記述があります。

「画面がAnonymousにチェックしていると、所属するAggregateやServer Actionはパブリックな（誰でもアクセスできる）APIになる」
「そうでない場合は、システムが自動でログインユーザーをベースに認証する仕組みを持つ」
後者はちょっとあいまいですが。

> When the screen where the aggregate or server action belongs to is made Public (the Anonymous role is selected), that REST API is also made Public.

> When the screen is not public (assigned to one or more roles except the Anonymous role), the system implements an authentication mechanism based on the logged-in user.

> When you have server actions that you expose to Reactive applications, you should always think about the possibility that the user is not correctly identified. Start an action with User and Roles validations on by using GetUserId() and Check<RoleName>Role(), to see if this user is allowed to execute this action.