2021年10月20日水曜日

Vanity URLからの管理ツール、開発ツールのアクセスをブロックする

 Oracle Autonomous Databaseのプロダクト・マネージャTodd Bottgerのブログ記事Oracle Autonomous Database Vanity URLs Part 2: Blocking Developer and Administrator Toolsの内容を確認してみました。

確認に使用した環境は元記事とは異なり(プライベート・エンドポイントでADBを構成するには費用がかかるため)、こちらの記事で作成した環境を使っています。


サンプル・アプリケーションを準備する

Oracle APEXの環境およびワークスペースが作成された直後を想定していますが、すでにワークスペースにアプリケーションが作成済みであれば、それをサンプルとして使うこともできます。その場合は、以下のサンプル・アプリケーションの作成作業は実施不要です。

今のところVanity URL経由でも管理ツールや開発ツールにアクセスできますが、Oracle CloudのコンソールからOracle APEXにアクセスして作業を進めます。


テストに使うためのアプリケーションを作成します。SQLワークショップのユーティリティに含まれるサンプル・データセットを開きます。

EMP/DEPTインストールし、アプリケーションの作成までを実行します。


アプリケーション・ビルダーを開き、作成されたアプリケーションを確認します。


アプリケーションのプロパティである簡易URLがデフォルトでONであるため、アプリケーションにアクセスするURLにはアプリケーションの別名が含まれます。アプリケーションの別名を確認するため、アプリケーション・プロパティの編集画面を開きます。


アプリケーションの別名を確認します。アプリケーションの別名に日本語が使用されている場合は、英数字と一部の記号(ハイフンなど)に限定するように別名を変更します。今回の例ではサンプル・データセットのEMP/DEPTを日本語でインストールしています。そのため別名がデモ - 従業員 / 部門となっています。これはDEMONSTRATION-EMP-DEPTに変更します。


作成したアプリケーションがVanity URL経由でアクセスできることを確認します。URLは以下の形式に従います。

https://Vanity URLとなるホスト名/ords/r/<ワークスペース名>/<アプリケーション別名/

EMP/DEPTのアプリケーションでは以下になります。ワークスペース名がAPEXDEVであると仮定しています。

https://<ホスト名>/ords/r/apexdev/demonstration-emp-dept/


以上で事前作業は完了です。これ以降、ロード・バランサの設定を行います。

デフォルトのアプリケーションを変更する


Oracle APEXのルートとなるURLにアクセスすると、開発ツールへのサインイン画面が開きます。以下のようなURLにアクセスします。

https://<ホスト名>/ords/


Vanity URLを構成して外部にサービスを公開している場合、開発ツールへのサインイン画面がデフォルトというのは望ましくありません。本来はポータルとなるようなアプリケーションを作成し、そのアプリケーションをデフォルトのアプリケーションとすべきですが、今回はEMP/DEPTのアプリケーションをデフォルトにしてみます。

Oracle Cloudのコンソールより、ネットワーキングロード・バランサを開きます。作成済みのロード・バランサを開きます。以前の記事の通りに作業を行なっていると、名前はapexlbになります。


ロード・バランサの詳細画面より、リソースルール・セットを選択します。作成済みのルール・セットの一覧が表示されます。ルール・セットの作成をクリックします。


ドロアーとしてルール・セットの作成が開きます。名前ADBPublicAccessとします。URLリダイレクト・ルールの指定チェックを入れると、URLリダイレクト・ルールの設定項目が開きます。

ソース・パス/ords/とし、一致タイプ接尾後一致を選択します。URLが/ords/で終了しているときに、このルールが適用されます。パス/ords/r/apexdev/demonstration-emp-dept/問合せ空白にし、レスポンス・コード302 - Foundを選択します。

作成をクリックします。


ブラウザが/ords/にアクセスすると、ロード・バランサは/ords/r/apexdev/demonstration-emp-dept/へリダイレクトするよう応答します。

ステータス成功になったら、ダイアログを閉じます。


ルール・セットが作成されたことを確認します。この状態では、まだ作成したルール・セットは適用されていません。


元記事ではルール・セットの追加設定を継続し、最後にリスナーにルール・セットを適用しています。設定の効果を感じないと作業を継続する気持ちが減るので、この記事ではとりあえず現状でリスナーにルール・セットを適用します。

リスナーを開いて、作成したルール・セットをリスナーに適用します。その後、ルール・セットが有効になります。リスナーの編集を実行します。


リスナーの編集のドロワーが開くので、ルール・セット追加ルール・セットをクリックします。


先ほど作成したルール・セットADBPublicAccessルール・セットに含め、変更の保存をクリックします。


ステータス成功になったらダイアログを閉じます。


適用されたルール・セットを確認するために、先ほどと同じURLにアクセスします。設定したアプリケーション(今回の例ではデモ - 従業員 / 部門のアプリケーション)が開きます。



APEXのツールをブロックする


APEXの管理ツールには、以下のURLでアクセスできます。

https://<ホスト名>/ords/apex_admin


外部に公開しているURLから管理ツールや開発ツールの画面が開けるのは望ましくありません。Vanity URLからの、これらのツールのアクセスをブロックします。

元記事によると、Oracle APEXのインスタンス・パラメータRESTRICT_DEV_HEADERとしてヘッダー名を設定すると、そのヘッダーを含んだHTTPリクエストによるAPEXの管理ツールおよび開発ツールのアクセスが制限される、という機能があるとのことです。

マニュアルのAvailable Parameter Valueに記載のないパラメータなのですが、オラクルのプロダクト・マネージャが書いた記事に載っている機能なので問題なく使用できるでしょう。

以下のスクリプトにより、パタメータRESTRICT_DEV_HEADERとしてADB-Public-Accessを設定します。
begin
    apex_instance_admin.set_parameter('RESTRICT_DEV_HEADER', 'ADB-Public-Access');
    commit;
end;
/
Oracle Cloudのコンソールからデータベース・アクションを開きます。


開発SQLを開きます。


ワークシートに先ほどのスクリプトを貼り付けスクリプトの実行をクリックします。PL/SQL procedure successfully completed.と表示されると設定完了です。


以下のスクリプトを同様に実行すると、設定済みのRESTRICT_DEV_HEADERを確認できます。
begin
    dbms_output.put_line(apex_instance_admin.get_parameter('RESTRICT_DEV_HEADER'));
end;
/

ロード・バランサを経由したリクエストすべてに、HTTPヘッダーとしてADB-Public-Accessを追加します。

先ほど作成したルール・セットADBPublicAccessの詳細画面を開きます。編集をクリックします。


リクエスト・ヘッダー・ルールの指定チェックを入れると、リクエスト・ヘッダー・ルールの設定画面が開きます。

アクションとしてリクエスト・ヘッダーの追加を選択し、ヘッダーにRESTRICT_DEV_HEADERとして設定済みのADB-Public-Accessを指定し、1とします。この設定によりロード・バランサを経由するリクエストにHTTPヘッダーADB-Public-Accessが追加されます。

変更の保存をクリックします。


ステータス成功になったら、先ほどのURLにアクセスしてみます。


先ほどと同じURLで管理ツールにアクセスすると、404 Not Foundのエラーが発生します。開発ツールも同様にブロックされます。


データベース・アクションをブロックする


データベース・アクションは以下のURLからアクセスします。

https://<ホスト名>/ords/sql-developer

サインイン後のURLは以下になります。

https://<ホスト名>/ords/<ユーザー名>/_sdw/


データベース・アクションへのアクセスをブロックするために、ルール・セットADBPublicAccessにリダイレクト・ルールを追加します。ルール・セットの編集画面を開き、別のURLリダイレクト・ルールをクリックします。


ソース・パスとして/_sdw/を指定します。一致タイプ接尾語一致です。リダイレクト先パス/ords/blockedとします。この宛先は存在しないため、ORDSは404 - Not Foundを返します。レスポンス・コード302 - Foundを選択します。

変更の保存をクリックします。


変更が成功したら、データベース・アクションにサインインしてみます。ユーザー名およびパスワードの入力をしてサインインすると、以下のように404 Not Foundが発生します。


エラー・メッセージは、blockedという名前のプロシージャにアクセスできませんでした。となっており、設定した通りにデータベース・アクションへのアクセスがブロックされていることが確認できます。

REST対応SQLをブロックする


最初にREST対応SQLを有効にします。Oracle Cloudのコンソールからデータベース・アクションにアクセスし、管理データベース・ユーザーを開きます。


REST対応SQLを有効にするユーザーのハンバーガー・メニューを開き、RESTの有効化を実行します。この例ではAPEXDEVというユーザーのREST対応を有効にしています。


REST対応ユーザーをクリックすると、そのスキーマのオブジェクトに対してREST対応SQLを実行できるようになります。


スキーマAPEXDEVに表EMPがあれば、以下のcurlコマンドを実行することにより表EMPの内容を取り出すことができます。

curl -X "POST" "https://<ホスト名>/ords/apexdev/_/sql" --header "Content-Type: application/sql" --user APEXDEV:APEXDEVのパスワード --data $'SELECT * FROM emp'

(以下は出力行数を制限しています)

% curl -X "POST" "https://<hostname>/ords/apexdev/_/sql" --header "Content-Type: application/sql" --user APEXDEV:************* --data $'SELECT * FROM emp where rownum < 2'

{"env":{"defaultTimeZone":"UTC"},"items":[{"statementId":1,"statementType":"query","statementPos":{"startLine":1,"endLine":2},"statementText":"SELECT * FROM emp where rownum < 2","resultSet":{"metadata":[{"columnName":"EMPNO","jsonColumnName":"empno","columnTypeName":"NUMBER","precision":4,"scale":0,"isNullable":0},{"columnName":"ENAME","jsonColumnName":"ename","columnTypeName":"VARCHAR2","precision":50,"scale":0,"isNullable":1},{"columnName":"JOB","jsonColumnName":"job","columnTypeName":"VARCHAR2","precision":50,"scale":0,"isNullable":1},{"columnName":"MGR","jsonColumnName":"mgr","columnTypeName":"NUMBER","precision":4,"scale":0,"isNullable":1},{"columnName":"HIREDATE","jsonColumnName":"hiredate","columnTypeName":"DATE","precision":0,"scale":0,"isNullable":1},{"columnName":"SAL","jsonColumnName":"sal","columnTypeName":"NUMBER","precision":7,"scale":2,"isNullable":1},{"columnName":"COMM","jsonColumnName":"comm","columnTypeName":"NUMBER","precision":7,"scale":2,"isNullable":1},{"columnName":"DEPTNO","jsonColumnName":"deptno","columnTypeName":"NUMBER","precision":2,"scale":0,"isNullable":1}],"items":[{"empno":7839,"ename":"中島 亜希子","job":"社長","mgr":null,"hiredate":"1981-11-17T00:00:00Z","sal":5000,"comm":null,"deptno":10}],"hasMore":false,"limit":10000,"offset":0,"count":1},"response":[],"result":0}]}


% 


スキーマのパスワードといった認証は必要で誰でもSQLを実行できるわけではありませんが、公開されているサイトではREST対応SQLは不要な場合が多いでしょう。

REST対応SQLのアクセスをブロックします。ルール・セットADBPublicAccessの編集画面を開き、別のURLリダイレクト・ルールをクリックします。

ソース・パス/_/sqlとします。それ以外はデータベース・アクションをブロックしたルールと同じ設定を行います。変更の保存をクリックします。


再度、REST対応SQLを実行してみます。通信内容を確認するため、-vオプションを追加します。

% curl -v -X "POST" "https://<hostname>/ords/apexdev/_/sql" --header "Content-Type: application/sql" --user APEXDEV:********* --data $'SELECT * FROM emp where rownum < 2'

Note: Unnecessary use of -X or --request, POST is already inferred.

*   Trying 158.101.94.209...

* TCP_NODELAY set

* Connected to www.**********.dev (158.101.94.209) port 443 (#0)

* ALPN, offering h2

* ALPN, offering http/1.1

* successfully set certificate verify locations:

*   CAfile: /etc/ssl/cert.pem

  CApath: none

* TLSv1.2 (OUT), TLS handshake, Client hello (1):

* TLSv1.2 (IN), TLS handshake, Server hello (2):

* TLSv1.2 (IN), TLS handshake, Certificate (11):

* TLSv1.2 (IN), TLS handshake, Server key exchange (12):

* TLSv1.2 (IN), TLS handshake, Server finished (14):

* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):

* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):

* TLSv1.2 (OUT), TLS handshake, Finished (20):

* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):

* TLSv1.2 (IN), TLS handshake, Finished (20):

* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256

* ALPN, server accepted to use http/1.1

* Server certificate:

*  subject: CN=*******.dev

*  start date: Oct  4 00:00:00 2021 GMT

*  expire date: Jan  2 23:59:59 2022 GMT

*  subjectAltName: host "www.*********.dev" matched cert's "www.********.dev"

*  issuer: C=AT; O=ZeroSSL; CN=ZeroSSL RSA Domain Secure Site CA

*  SSL certificate verify ok.

* Server auth using Basic with user 'APEXDEV'

> POST /ords/apexdev/_/sql HTTP/1.1

> Host: www.apexugj.dev

> Authorization: Basic QVBF*********************zkyTg==

> User-Agent: curl/7.64.1

> Accept: */*

> Content-Type: application/sql

> Content-Length: 34

> 

* upload completely sent off: 34 out of 34 bytes

< HTTP/1.1 302 Moved Temporarily

< Date: Wed, 20 Oct 2021 06:34:34 GMT

< Content-Type: text/html

< Content-Length: 133

< Connection: keep-alive

< Location: https://www.*********.dev:443/ords/blocked

< 

<html>

<head><title>302 Found</title></head>

<body>

<center><h1>302 Found</h1></center>

<hr><center></center>

</body>

</html>

* Connection #0 to host www.apexugj.dev left intact

* Closing connection 0

ynakakoshi@yujis-macbook-pro Downloads % 


レスポンスとして302を受け取っていて、リダイレクト先であるLocationとして/ords/blockedが返されていることが確認できます。

以上で今回の記事は終了です。

Oracle APEXのシステム構成作業の参考になれば幸いです。