2024年7月24日水曜日

ブラウザ上のページ・アイテムの値や動的アクションによってかけた保護を回避する

ブラウザ上で選択できるページ・アイテムの値により表示できるデータを制限したり、動的アクションでユーザーが実行できる操作を制限することがあります。これらの実装はセキュリティの観点からは補助的なものであり、ブラウザ上でJavaScriptのコードを直接実行することにより回避できます。

これはOracle APEX特有の脆弱性というわけではなく、Webアプリケーションであれば何でも当てはまるはずです。10年以上前は「ブラウザでJavaScriptの実行は禁止する」ということも行われていましたが、今は現実的な対応とはいえません。

ユーザーに近いブラウザ側で制約をかけた方がアプリケーションは使いやすくなりますが、悪意のあるユーザーからデータを保護するといったセキュリティ上の対策はサーバー側に実装する必要があります。

いくつか例を紹介します。

サンプル・データセットEMP/DEPTに含まれる表EMPソースとしたクラシック・レポートを作成します。

一覧する従業員の絞り込みに使用するページ・アイテムとしてP1_DEPTNOを作成します。部門の値を選択すると、その部門に所属する従業員のみがレポートに一覧されます。


ページ・アイテムP1_DEPTNOタイプとして選択リストを選び、LOVSQL問合せとして以下を記述します。

select dname d, deptno r from dept where deptno <> 10


ページ・アイテムP1_DEPTNOの値が変更されたときにレポートがリフレッシュされるように、動的アクションを作成しています。

レポートのWHERE句deptno = :P1_DEPTNOという条件を設定し、送信するページ・アイテムP1_DEPTNOを指定します。


これでレポートとしてはページ・アイテムP1_DEPTNOで指定された従業員しか一覧せず、ページ・アイテムP1_DEPTNOではDEPTNOから10(部門名はACCOUNTING)を除外しているため、ブラウザからの操作では部門ACCOUNTINGの従業員は一覧できません。

以下が通常の操作です。部門としてRESEARCH、SALES、OPERATIONSのどれかが選択可能で、選択した部門の従業員が一覧されます。


このレポートに部門ACCOUNTINGの従業員を一覧させます。以下のコードを実行します。ページ・アイテムP1_DEPTNOの選択リストにオプションとしてACCOUNTING(値は10)を追加した上で、P1_DEPTNOに10を設定します。
var select = document.getElementById("P1_DEPTNO");
var option = document.createElement("option");
option.text = "ACCOUNTING";
option.value = "10";
select.add(option);
apex.items.P1_DEPTNO.value = 10;
部門ACCOUNTINGに所属している従業員が一覧されます。


レポートのWHERE句を以下に変更し、DEPTNOが10の従業員はつねに一覧に表示しないようにすると、ブラウザ側で何をしても部門ACCOUNTINGの従業員は表示されません。

deptno = :P1_DEPTNO and deptno != 10


簡単ですが、データの保護をサーバー側で実装する一例です。

続いて動的アクションによるデータの保護を回避します。

ボタンRefeshをクリックすると従業員のレポートがリフレッシュされ、従業員の一覧が表示されます。その際に動的アクションで表示条件を設定しています。


ボタンREFRESHをクリックしたときに実行される最初のTRUEアクションで以下のJavaScriptコードを実行し、P1_ALLOW0を設定します。必ず0を設定することになるため、ボタンREFRESHを押しても従業員の一覧が表示されることはありません。一般的には、特定の従業員であれば、とか、同じ部門であれば、といった条件を付けるでしょう。
if ( true ) {
    apex.items.P1_ALLOW.value = 0;
}
else
{
    apex.items.P1_ALLOW.value = 1;
}

レポートのWHERE句に以下の条件を設定します。

1 = :P1_ALLOW

送信するページ・アイテムとしてP1_ALLOWを設定します。


このレポートに従業員を一覧させます。以下のJavaScriptのコードを実行します。名前がonClick Refreshという動的アクションを見つけて、先頭のTRUEアクション削除します。その後にP1_ALLOW1を設定します。
var da = apex.da.gEventList.find(item => item.name === "onClick Refresh");
 delete da.actionList[0];
 apex.items.P1_ALLOW.value = 1;
JavaScriptのコードを実行した後にボタンRefreshをクリックすると、P1_ALLOWを0に設定するTRUEアクションが削除されているため、P1_ALLOWは1から変わりません。結果としてレポートに従業員が一覧されます。


例えばユーザーごとに表示/非表示の条件を設定したいのであれば、認可スキームの設定などが簡単な対応になります。


今回の記事は以上になります。

簡単なアプリケーションですが、今回の記事で使用したアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/test-browser-side-condition.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。