JavaScript Security Best Practices
Details concerning the best practices when using features of MLE with JavaScript are described.
Topics
- Using Bind Variables for Security and Performance
- Generic Database and PL/SQL Specific Security Considerations
- Supply Chain Security
- Software Bill of Material
- Using the Database to Store State
- Disabling Multilingual Runtime
Parent topic: MLE Security
Using Bind Variables for Security and Performance
The MLE JavaScript SQL driver allows you to use string concatenation to build SQL commands, including the predicates used in queries and DML statements. It is strongly recommended to avoid this bad practice as it is a major source for SQL injection attacks. Not only is the use of bind variables in SQL statements more secure than string concatenation but it is also more efficient as it allows the database to reuse the cursor in the shared pool.
If it is not possible to avoid the creation of dynamic SQL, ensure that you validate
input to your code and scan for malicious content. The built-in
DBMS_ASSERT
package provides a wealth of functions designed to
mitigate against SQL injection attacks. It does not offer complete protection but
its use is very much recommended as it allows you to verify the following:
-
The input string is a qualified SQL name
-
The input string is an existing schema name
-
The input string is a simple SQL name
-
The input parameter string is a qualified SQL identifier of an existing SQL object
The use of bind variables for better security and scalability is not limited to a single programming language such as JavaScript, it equally applies to every development project using Oracle Database.
See Also:
-
Server-Side JavaScript API Documentation for information about using bind variables with
mle-js-oracledb
-
Oracle Database Development Guide for more details regarding bind variables and their impact on performance and security
Example 10-2 Using Bind Variables Rather than String Concatenation
In this example, the SELECT
statement accepts a bind variable rather
than concatenation the input variable, managerID
, to the SQL
command.
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];
}
/
Example 10-3 Use DBMS_ASSERT to Verify Valid Input
In this example, the function createTempTable()
creates a private
temporary table to hold intermediate results from a batch process. The function
takes a single argument: the name of the temporary table to be created (minus the
prefix). The function checks if the parameter passed to it is a valid SQL name.
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)`
);
}
/
If the table name passed to the function passes the test, it is then used to create a
private temporary table using the default
private_temp_table_prefix
.
Parent topic: JavaScript Security Best Practices
Generic Database and PL/SQL Specific Security Considerations
Because all JavaScript code is accessed eventually via a PL/SQL call specification, it is important to understand the implications of using PL/SQL as well. The following concepts are of particular importance:
-
The difference between invoker's rights and definer's rights
-
Code Based Access Control (CBAC)
-
The impact of
INHERIT PRIVILEGES
in invoker's rights code -
Role grants and direct grants, both object as well as system privileges
You should always aim to only require the minimum security privileges (object and system) for JavaScript code to execute. This is especially important when you consider the use of external third-party JavaScript code.
Administrators should consider the use of encryption for both data at rest as well as data in motion.
See Also:
-
Oracle Database Security Guide for more information about generic database-related security aspects
- Oracle Database Transparent Data Encryption Guide for information about encrypting data at rest using Transparent Data Encryption (TDE)
Parent topic: JavaScript Security Best Practices
Supply Chain Security
Access to the rich community ecosystem is one of the advantages of using JavaScript in Oracle Database. Rather than creating functionality in-house and potentially duplicating effort, existing JavaScript can be used instead. While this is a convenient method for developing applications, it comes with certain risks.
In past years, the term supply chain attach has been used to describe the fact that certain popular open-source JavaScript modules have been abandoned by the original maintainers. Bad actors have taken some of these projects, becoming maintainers but only to inject malicious code into the source. The next time a project references such a compromised module, they incorporate the malicious code.
The same principles applied to client-side development apply to server-side development with MLE. Developers and security teams must be aware that code in the application executes with potentially elevated privileges. These can be abused by malicious code to compromise confidentiality, integrity, and availability properties of the application. For that reason, extra care must be taken to ensure third-party code is trustworthy and that the minimum number of privileges is granted to it. Many companies have a dedicated security team for vetting open-source modules prior to granting their approval to use them. At the very least, you should audit the JavaScript code that you are about to include in your project and document the result.
It is possible to lock a given version of an open-source module using a mechanism
like the package-lock.json
file so as not to get caught out if a
new version of a module is distributed. Automatically pulling the latest version of
an external code dependency is bad practice and should always be avoided.
In the case of JavaScript in MLE, JavaScript code executes with the database privileges that are in effect for the associated execution context. JavaScript code can retrieve and modify data stored in the database according to these privileges. Malicious code can leverage these privileges to modify the database in an inappropriate manner.
As a consequence, be sure to grant the privileges to create MLE modules carefully and only grant these in
environments where they are essential. If possible, avoid granting the
[CREATE
| ALTER
| MODIFY
]
ANY
system privileges at all.
You should also review the INHERIT PRIVILEGES
settings in the
context of invoker's rights procedures. Once the settings for INHERIT
PRIVILEGES
are reviewed and secured according to industry best
practice, consider the use of invoker's rights for MLE call specifications.
Additional higher levels of security for invoker's rights procedures can be achieved by implementing code based access control (CBAC). Using CBAC, developers can associate roles to PL/SQL units without having to elevate the privileges of the schema or invoker.
See Also:
Oracle Database Security
Guide for
details about the INHERIT PRIVILEGES
privilege
Parent topic: JavaScript Security Best Practices
Software Bill of Material
Every project relying on external code in projects is strongly encouraged to maintain a record of all software components (including versions) that are bundled in a deployed application artifact.
The software bill of material (SBOM) is the key tool to use when reacting swiftly to a newly published vulnerability is of utmost importance. Exploits are almost guaranteed to be used immediately after a vulnerability has been published. Knowing exactly which version of a third-party library is in use allows you to save crucial time in preparing a response.
In addition to storing the actual code, MLE modules feature a metadata field that can be used to store arbitrary metadata with the module. In particular, it can be used to store an SBOM that describes all JavaScript libraries bundled in the module. The field is not interpreted by the MLE runtime. Content and format are entirely up to you.
See Also:
MLE JavaScript Modules and Environments for more information about creating MLE modules and providing metadata to them
Parent topic: JavaScript Security Best Practices
Using the Database to Store State
Applications written using MLE JavaScript code should not deviate from established patterns such as storing application state in tables. This allows you to make the best use of the rich number of security features available for Oracle Database.
In particular, you should not rely on JavaScript state that exceeds the boundaries of one stored procedure or function call.
Oracle Database has great support for JSON, offering both a relational as well as a NoSQL API. The database's JSON API is a natural candidate for MLE JavaScript code to store state. Storing state in Oracle Database provides a better programming model than application state, especially when it come to data persistence and transactional consistency.
See Also:
Oracle Database JSON Developer’s Guide for information about using JSON with Oracle Database
Example 10-4 Using Bind Variables Rather than String Concatenation
In this example, the SELECT
statement accepts a bind variable rather
than concatenation the input variable, managerID
, to the SQL
command.
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];
}
/
Example 10-5 Use DBMS_ASSERT to Verify Valid Input
In this example, the function createTempTable()
creates a private
temporary table to hold intermediate results from a batch process. The function
takes a single argument: the name of the temporary table to be created (minus the
prefix). The function checks if the parameter passed to it is a valid SQL name.
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)`
);
}
/
If the table name passed to the function passes the test, it is then used to create a
private temporary table using the default
private_temp_table_prefix
.
Parent topic: JavaScript Security Best Practices
Disabling Multilingual Runtime
In the case where a security vulnerability is detected in JavaScript
code, you can prevent JavaScript code from execution by disabling the JavaScript
runtime. Setting the initialization parameter MLE_PROG_LANGUAGES
to
OFF
does not stop the database from accepting new code (such
behavior prevents the implementation of a code fix) but it does stop anyone from
executing JavaScript code.
Applications should be written with that option in mind. Once the MLE runtime is disabled, an error is thrown. Rather than showing the raw error to the end user, a more accessible error message should be created.
Although JavaScript does not have a specific lockdown feature, using the
MLE_PROG_LANGUAGES
parameter allows you to disable the MLE runtime at the session, PDB (lockdown
profiles operate at this level), or CDB level. The
COMMON_SCHEMA_ACCESS
feature bundle in the lockdown profile can
be used to disable MLE DDL.
Parent topic: JavaScript Security Best Practices