JavaScriptセキュリティのベスト・プラクティス

JavaScriptでMLEの機能を使用する場合のベスト・プラクティスに関する詳細を説明しています。

トピック

セキュリティおよびパフォーマンスのためのバインド変数の使用

MLE JavaScript SQLドライバを使用すると、文字列の連結を使用して、問合せおよびDML文で使用される述語を含むSQLコマンドを作成できます。この方法はSQLインジェクション攻撃の主な発生原因となるため適切ではなく、使用しないことをお薦めします。SQL文でのバインド変数の使用は、文字列の連結よりも安全であり、さらにデータベースが共有プールでカーソルを再利用できるため、より効率的です。

動的SQLの作成を回避できない場合は、必ず、コードへの入力を検証し、悪意のあるコンテンツがないかどうかをスキャンするようにします。組込みのDBMS_ASSERTパッケージは、SQLインジェクション攻撃を軽減するように設計された豊富な機能を提供します。完全な保護は提供しませんが、次のことを検証できるため、使用することをお薦めします:

  • 入力文字列が修飾SQL名であること

  • 入力文字列が既存のスキーマ名であること

  • 入力文字列が単純SQL名であること

  • 入力パラメータ文字列が既存のSQLオブジェクトの修飾SQL識別子であること

セキュリティおよびスケーラビリティを向上させるためにバインド変数を使用することは、JavaScriptなどの1つのプログラミング言語に限定されず、Oracle Databaseを使用するすべての開発プロジェクトに同様に適用されます。

関連項目:

例10-2 文字列の連結ではなくバインド変数を使用する

この例では、SELECT文は、入力変数managerIDをSQLコマンドに連結するのではなく、バインド変数を受け入れます。

CREATE OR REPLACE MLE MODULE select_bind LANGUAGE JAVASCRIPT AS

import oracledb from "mle-js-oracledb";

export function numEmployeesByManagerID(managerID) {

  const conn = oracledb.defaultConnection(managerID);
  const result = conn.execute(
    `SELECT count(*) FROM employees WHERE manager_id = :1`,
    [ managerID ]
  );

  return result.rows[0][0];
}
/

例10-3 DBMS_ASSERTを使用した有効な入力の検証

この例では、ファンクションcreateTempTable()によって、バッチ・プロセスの中間結果を保持するプライベート一時表が作成されます。このファンクションでは、1つの引数(作成する一時表の名前から接頭辞を除いたもの)を使用します。このファンクションは、渡されたパラメータが有効なSQL名かどうかをチェックします。

CREATE OR REPLACE MLE MODULE dbms_assert_module LANGUAGE JAVASCRIPT AS

import oracledb from "mle-js-oracledb";

export function createTempTable(tableName) {
  const conn = oracledb.defaultConnection();
  let result; 
  let validTableName; 

  try {
    result = conn.execute(
      `SELECT dbms_assert.qualified_sql_name(:tableName)`, 
      [tableName]
    );
    validTableName = result.rows[0][0];
  } catch (err) {
    throw (`'${tableName}' is not a valid table name`);
    return;
  }

  result = conn.execute(
    `CREATE PRIVATE TEMPORARY TABLE ora\$ptt_${validTableName} (id number)`
  );
}
/

ファンクションに渡された表名がテストに合格した場合は、この表名を使用して、デフォルトのprivate_temp_table_prefixを使用してプライベート一時表が作成されます。

汎用データベースおよびPL/SQL固有のセキュリティに関する考慮事項

すべてのJavaScriptコードは最終的にPL/SQLコール仕様を介してアクセスされるため、PL/SQLを使用する意味を理解することも重要です。次の概念は特に重要です:

  • 実行者権限と定義者権限の違い

  • コード・ベース・アクセス制御(CBAC)

  • 実行者権限コードにおけるINHERIT PRIVILEGESの影響

  • ロール付与と直接付与(オブジェクト権限とシステム権限の両方)

JavaScriptコードの実行には、常に最小限のセキュリティ権限(オブジェクトおよびシステム)のみが必要になるようにしてください。これは、外部のサード・パーティのJavaScriptコードの使用を検討する場合に特に重要です。

管理者は、保存中のデータと移動中のデータの両方に暗号化を使用することを検討する必要があります。

関連項目:

サプライ・チェーンのセキュリティ

豊富なコミュニティ・エコシステムへのアクセスは、Oracle DatabaseでJavaScriptを使用する利点の1つです。機能を社内で作成する(重複作業が発生する可能性もあるある)のではなく、かわりに既存のJavaScriptを使用できます。これはアプリケーションを開発する方法として便利ではありますが、特定のリスクを伴います。

過去数年間、人気の高い特定のオープン・ソースのJavaScriptモジュールが元のメンテナによって放棄されたという事実を表現するサプライ・チェーン・アタッチという用語が使用されてきました。残念ながら、悪意のあるアクターがこれらのプロジェクトのいくつかのメンテナになり、悪意のあるコードをソースに挿入しています。その後、プロジェクトがそのような危険性のあるモジュールを参照すると、悪意のあるコードが組み込まれます。

クライアント側の開発に適用されるのと同じ原則が、MLEを使用したサーバー側の開発にも適用されます。開発者およびセキュリティ・チームは、アプリケーション内のコードが実行されるときの権限が昇格される可能性があることを認識する必要があります。これらは悪意のあるコードによって悪用され、アプリケーションの機密性、整合性および可用性のプロパティが不正使用される可能性があります。そのため、サード・パーティのコードが信頼できること、および最小数の権限が付与されていることを慎重に確認する必要があります。多くの企業には、オープン・ソース・モジュールの使用を承認する前に審査する専用のセキュリティ・チームがあります。少なくとも、プロジェクトに含めるJavaScriptコードを監査し、結果を文書化する必要があります。

package-lock.jsonファイルのようなメカニズムを使用して、オープン・ソース・モジュールの特定のバージョンをロックして、モジュールの新しいバージョンが配布された場合に更新対象にならないようにできます。外部コードの依存関係の最新バージョンを自動的にプルする方法は好ましくないため、常に避ける必要があります。

MLEのJavaScriptの場合、JavaScriptコードは、関連付けられた実行コンテキストに対して有効なデータベース権限で実行されます。JavaScriptコードは、これらの権限に従って、データベースに格納されているデータを取得および変更できます。悪意のあるコードはこれらの権限を利用して、不適切な方法でデータベースを変更する可能性があります。

したがって、MLEモジュールを作成する権限は慎重に付与し、必要不可欠な環境でのみ付与してください。可能な場合は、[CREATE | ALTER | MODIFY] ANYシステム権限を付与しないようにしてください。

また、実行者権限プロシージャのコンテキストでINHERIT PRIVILEGES設定を確認する必要があります。業界のベスト・プラクティスに従ってINHERIT PRIVILEGESの設定を確認および保護した後に、MLEコール仕様に実行者権限を使用することを検討してください。

コード・ベース・アクセス制御(CBAC)を実装することで、実行者権限プロシージャに対するより高いレベルの追加のセキュリティを実現できます。CBACを使用すると、開発者は、スキーマまたは実行者の権限を昇格させる必要なく、ロールをPL/SQLユニットに関連付けることができます。

関連項目:

INHERIT PRIVILEGES権限の詳細は、『Oracle Databaseセキュリティ・ガイド』を参照してください

ソフトウェア部品構成表

プロジェクトに含まれる外部コードに依存するすべてのプロジェクトは、デプロイ済アプリケーション・アーティファクトにバンドルされているすべてのソフトウェア・コンポーネントのレコード(バージョンを含む)を保持するようにすることをお薦めします。

ソフトウェア部品構成表(SBOM)は、新しく公開された脆弱性に迅速に対応することが最も重要な場合に使用する主要なツールです。エクスプロイトは、脆弱性が公開された直後にほぼ確実に使用されます。使用中のサード・パーティ・ライブラリのバージョンを正確に把握しておくことで、対応の準備にかかる時間を大幅に節約できます。

実際のコードの格納に加えて、MLEモジュールにはメタデータ・フィールドがあり、このフィールドを使用して任意のメタデータをモジュールに格納できます。特に、モジュールにバンドルされているすべてのJavaScriptライブラリを記述するSBOMを格納するために使用できます。このフィールドは、MLEランタイムによって解釈されません。コンテンツとフォーマットは全体的に任意です。

関連項目:

MLEモジュールの作成およびモジュールへのメタデータの提供の詳細は、「MLE JavaScriptモジュールおよび環境」を参照してください

データベースを使用した状態の格納

MLE JavaScriptコードを使用してアプリケーションを記述する場合は、アプリケーション状態を表に格納するなどの確立されたパターンから逸脱しないようにします。これにより、Oracle Databaseで使用可能な豊富なセキュリティ機能を最大限に活用できます。

特に、1つのストアド・プロシージャまたはファンクション・コールの境界を越えるJavaScript状態に依存しないようにしてください。

Oracle DatabaseはJSONを強力にサポートしており、リレーショナルとNoSQL APIの両方を提供しています。データベースのJSON APIは、状態を格納するMLE JavaScriptコードでは当然の候補です。Oracle Databaseに状態を格納すると、特にデータの永続性およびトランザクションの一貫性に関して、アプリケーション状態よりも優れたプログラミング・モデルが提供されます。

関連項目:

Oracle DatabaseでのJSONの使用の詳細は、『Oracle Database JSON開発者ガイド』を参照してください

例10-4 文字列の連結ではなくバインド変数を使用する

この例では、SELECT文は、入力変数managerIDをSQLコマンドに連結するのではなく、バインド変数を受け入れます。

CREATE OR REPLACE MLE MODULE select_bind LANGUAGE JAVASCRIPT AS

import oracledb from "mle-js-oracledb";

export function numEmployeesByManagerID(managerID) {

  const conn = oracledb.defaultConnection(managerID);
  const result = conn.execute(
    `SELECT COUNT(*) FROM employees WHERE manager_id = :1`,
    [ managerID ]
  );

  return result.rows[0][0];
}
/

例10-5 DBMS_ASSERTを使用した有効な入力の検証

この例では、ファンクションcreateTempTable()によって、バッチ・プロセスの中間結果を保持するプライベート一時表が作成されます。このファンクションでは、1つの引数(作成する一時表の名前から接頭辞を除いたもの)を使用します。このファンクションは、渡されたパラメータが有効なSQL名かどうかをチェックします。

CREATE OR REPLACE MLE MODULE dbms_assert_module LANGUAGE JAVASCRIPT AS

import oracledb from "mle-js-oracledb";

export function createTempTable(tableName) {
  const conn = oracledb.defaultConnection();
  let result; 
  let validTableName; 

  try {
    result = conn.execute(
      `SELECT dbms_assert.qualified_sql_name(:tableName)`, 
      [tableName]
    );
    validTableName = result.rows[0][0];
  } catch (err) {
    throw (`'${tableName}' is not a valid table name`);
    return;
  }

  result = conn.execute(
    `CREATE PRIVATE TEMPORARY TABLE ora\$ptt_${validTableName} (id number)`
  );
}
/

ファンクションに渡された表名がテストに合格した場合は、この表名を使用して、デフォルトのprivate_temp_table_prefixを使用してプライベート一時表が作成されます。

マルチリンガル・ランタイムの無効化

JavaScriptコードでセキュリティの脆弱性が検出された場合、JavaScriptランタイムを無効にしてJavaScriptコードが実行されないようにできます。初期化パラメータMLE_PROG_LANGUAGESOFFに設定すると、データベースが新しいコードを受け入れることは妨げられず、コード修正の実装を妨げるような動作は発生しませんが、JavaScriptコードの実行が停止されます。

アプリケーションは、そのオプションを考慮して記述する必要があります。MLEランタイムが無効になると、エラーがスローされます。エンド・ユーザーに未処理エラーを表示するのではなく、より理解しやすいエラー・メッセージを作成する必要があります。

JavaScriptには特定のロックダウン機能はありませんが、MLE_PROG_LANGUAGESパラメータを使用すると、セッション、PDB (ロックダウン・プロファイルはこのレベルで動作)またはCDBレベルでMLEランタイムを無効にできます。ロックダウン・プロファイルのCOMMON_SCHEMA_ACCESS機能バンドルを使用して、MLE DDLを無効にできます。