|
Developer JVM
Securing Java Execution
By Kuassi Mensah
A DBA's perspective on OracleJVM security mechanisms
Security is a prime concern for DBAs, CTOs, every architect and developer in between,
and every end user of a system that is running software modules close to that most valuable corporate assetyour business data. In contrast to
traditional JDK-based Java virtual machines, the Oracle Java Virtual Machine (OracleJVM) lets you run
Java objects inside Oracle9i Database, giving you all the performance
advantages of the Oracle database. But how does OracleJVM enforce Java security?
It turns out that Java code running in Oracle9i Database benefits from a rich security environment that you can fine-tune in a number of ways. You can take advantage of database security mechanisms such as user-authentication security, database-schema security, login-user security, and effective-user security. OracleJVM provides additional features such as class-resolution security and Java 2 security. We'll discuss each of these mechanisms below, but first let's take a quick look at OracleJVM itself.
OracleJVM
The OracleJVM execution environment, available since Oracle8i Release 8.1.5, lets Java objects (Java 2 Standard EditionJ2SE) run in the RDBMS kernel. OracleJVM is tightly integrated with Oracle's database-session architecture. This architecture reconciles the differences between a traditional JDK VM with its full J2SE compatibility
and the Oracle database with its own memory model, error handling, and scalability.
When Java code is first invoked in
a database session, the session transparently activates a virtually dedicated JVM in the database server process. Subsequent invocations of Java benefit from this already Java-enabled process. All sessions share read-only data
from the JVM system classes (java.*, oracle.aurora.*, oracle.jdbc.*.) as
well as from any other classes previously loaded into the database. This means that each user takes up a small memory footprint, and you can keep adding Java clients and still maintain linear scalability. By default, a lightweight, server-side JDBC driver is activated in every Java-enabled database session. The JDBC driver furnishes function-based, SQL data access.
OracleJVM maintains compatibility with J2SE. As of Oracle9i Release 2, OracleJVM is also compatible with J2SE 1.3.1. OracleJVM support for J2SE means that Oracle9i Database capabilities are extended by the use of Java libraries. For example, you can use the Java Secure Socket Extension (JSSE) within the OracleJVM to place Java HTTPS callouts to external systems.
Because Java is supported in the database, you can partition JDBC, SQLJ, and JMS applications across the middle tier and the database tier. You can also partition advanced data-bound J2SE applications that can bridge SQL; XML; Java 2 Platform, Enterprise Edition (J2EE); and Web services. (For more details, see Next Steps.)
User-Authentication Security
An Oracle9i Database user, typically a client application or a JDBC connection, needs to be identified, authenticated, and authorized before it can create
a session. Oracle Net and, to
some degree, the type 2 JDBC driver (also known as the OCI driver) support several user-authentication mechanisms. These methods include traditional password authentication, strong authentication mechanisms (such as Kerberos, CyberSafe, RADIUS, token cards, smart cards, Biometrics, PKI, and certificate-based authentication), proxy authentication and authorization, and Oracle Single Sign-On (SSO). Once the database client is authenticated, a new
database session is associated with
that requester. (For more details on user authentication, read the Oracle9i Release 2 security overview white papersee Next Steps.)
Database-Schema Security
Traditional JDK-based Java virtual machines let you execute Java classes directly downloaded from the Web. With OracleJVM, a Java class resides
in the database, so it's governed by the same security features that protect other database objects. Specifically, Java classes, sources, and resources
are organized, stored, searched, and executed within database schemas. Before you can execute a Java class with OracleJVM, you need to load it into a specific database schema and then validate it. To load a Java class, you can use the loadjava command-line utility or an IDE such as Oracle9i JDeveloper. You must have the CREATE PROCEDURE privilege to load classes into your own schema and the CREATE ANY PROCEDURE privilege to load classes into a schema owned by another user. These protections provide a reliable Java execution environment, less likely to be attacked by malicious code than a JDK-based JVM.
However, any Java class needs to share J2SE and system classes. These classes are loaded once, either in the PUBLIC schema or the SYSTEM schema. By definition, objects in the PUBLIC schema are accessible from any other schema. If the Java system classes are loaded into the SYSTEM schema, they're made accessible through PUBLIC synonyms, a database mechanism that provides public visibility to common objects or resources.
OracleJVM prevents users from replacing system classes or loading new classes into system packages. If
a user wants to load such classes
into another schema, the user must
be granted the JServerPermission (LoadClassInPackage.<class>) permission. The following command grants user SCOTT permission to load classes into the oracle.aurora.* package:
dbms_java.grant_permission('SCOTT', 'SYS:oracle.aurora.tools.*', null);
Java classes are owned by the schema in which they are loadedcalled the defining schema. You can provide different levels of security for Java classes by grouping them in separate schemas. For example, suppose you define class A
and class B in different schemas. In order for class A to refer to class B, the defining schema of class A must be granted execute rights on class B. To accomplish this, you can use the grant option in the loadjava utility. By creating new schemas for particular classes and controlling execute rights to those classes, you can design fine-grained security for your Java applications.
In the following example, Betty and Bob are granted the right to execute class alpha in the schema TEST:
loadjava -thin -schema test Ðu SCOTT/TIGER@localhost:5521:orcl
-grant BETTY,BOB alpha.class
Class-Resolution Security
OracleJVM uses a resolver specification (resolver spec) to search for and locate Java classes within database schemas. A resolver spec usually contains multiple schemas. It is similar in concept to the classpath in the JDK world. In contrast with JDK, resolver specs are defined
on a class-by-class basis. There is no general classpath. Instead, there's a default resolver spec for each schema.
The following examples show
different ways to define the scope of
a resolver spec:
Before a resolver spec can resolve a reference to a class, the class must be visible and the requester must have permission to execute it. Furthermore, OracleJVM requires that all references to other classes be resolved at deployment time (load time), unless it is told to defer resolutions until runtime. All references must be resolved before a class can be used. For example, the following resolver spec results in ignoring all classes not found within SCOTT or PUBLIC schemas:
loadjava -resolve -resolver "((* SCOTT)
(* PUBLIC))" -genmissing
genmissing is an option of loadjava that lets you deal with nonexistent classes. It instructs loadjava to create and load placeholder definitions of classes that are referenced but not defined. By resolving the references to the missing classes, the genmissing option allows the Java code to run, but since the missing classes have placeholder definitions, their methods cannot be invoked. Class-resolution architecture provides flexible and fine-grained control over class visibility
and security.
Login-User and Effective-User Security
When a user logs in to the database, a new session is created with a login-user identity or schema identity. All database operations, including SQL statements, PL/SQL packages, and PL/SQL wrappers for Java stored procedures, are executed under the login user identity. A session can execute Java classes defined in other schemas, provided the login user's schema has been granted execute permission on the other classes.
However, you can dynamically override the effective identity under which a Java class runs. By default, the effective identity is the login-user identity. You can alter the identity at the Java class level, or PL/SQL stored-procedure level, by using the database's effective-user mechanism, similar to the UNIX setuid facility.
You can set the effective identity by assigning invoker's rights or definer's rights to a class.
- Invoker's rights. By default, a Java class is associated with invoker's rights; the class is not bound to its defining schema. The current user of any session executing the class is the same as the session's login user. The privileges are checked at runtime, and external references are resolved in the schema of the current user. With invoker's rights, you can centralize
and share code across multiple schemas and applications while keeping data stored in each schema, private and isolated.
- Definer's rights. By assigning definer's rights to a class, you bind the class to its defining schema. The effective user of any session executing the class is changed temporarily to the identity of the class's defining schema, and all unqualified references are looked for
in the defining schema. Java classes loaded with definer's rights can be executed without requiring the invoking session to be connected as the schema to which the code belongs.
You can specify effective-user rights on the PL/SQL wrapper (by using Call Spec, which exposes public static Java methods to the SQL world), or on the Java class itself, by using the loadjava utility. These rights are in effect only when a Java class or PL/SQL package is invoked through server-side JDBC.
In the following examples, the effective-user rights are defined on the PL/SQL wrapper and on the Java class.
- Specifying invoker's rights (current user) through the PL/SQL interface:
CREATE [OR REPLACE] PROCEDURE [schema_name.]procedure_name
[(parameter_list)]
[AUTHID CURRENT_USER] AS ...
- Specifying invoker's rights (current user) on the Java class with loadjava:
loadjava {-user | -u} <user>/<password>[@<database>] [options]
<file>.java | <file>.class | <file>.jar |
<file>.zip |<file>.sqlj | <resourcefile> ...
[-nodefiner]
- Specifying definer's rights through the PL/SQL interface:
CREATE [OR REPLACE] PROCEDURE [schema_name.]procedure_name
[(parameter_list)]
AUTHID DEFINER ...
- Specifying definer's rights on the Java class with loadjava:
loadjava {-user | -u} <user>/<password>
[@<database>] [options]
<file>.java | <file>.class | <file>.jar |
<file>.zip |<file>.sqlj | <resourcefile> ...
-definer
For more details on effective-user rights, read Chapter 8 of the PL/SQL User's Guide and Reference (see Next Steps).
Java 2 Security
The goals of Java 2 security are to provide fine-grained access control, easily configurable security policy, an easily extensible access-control structure, and extension of security.
OracleJVM fully complies with
Java 2 security, but its implementation differs from JDK VM on the following points:
- Unlike JDK, OracleJVM security is based on the defining schema. That is, permissions are granted to the defining schema, not to the Java code base.
- Each Java 2 permission is represented by a permission object stored in the PolicyTable and identifiable by a row key. The PolicyTable is defined in a secure schema with restricted access. (By contrast, JDK maintains permissions in a policy file.) You must specify all security policies in the PolicyTable, which you can extend and update through the DBMS_JAVA package or PolicyTableManager methods, as shown in the following examples:
Call dbms_java.grant_permission(grantee,
permission_type, permission_name,
permission_action)
oracle.aurora.rdbms.security
.PolicyTableManager.grant(grantee,
permission_type, permission_name,
permission_action)
- Unlike JDKs, OracleJVM supports granting and revoking/restricting permissions. Only the JAVA_ADMIN user
can grant to other users the permission to access and update the PolicyTable.
- OracleJVM supports all Java 2 permissions as well as Oracle-defined
permissions. In addition, end users with proper privileges can define and store their own permissions.
- The SecurityManager is always
initialized and active in a Java-enabled session.
OracleJVM Security Best Practices
Oracle recommends that you take the following actions to maintain the security of your Java applications:
- Grant each permission explicitly. Taking a fine-grained approach lets you follow the spirit of Java 2 security. The JAVASYSPRIV and JAVAUSERPRIV roles are groups of permissions maintained for backward-compatibility purposes only.
- Grant java.io.FilePermission only
to explicitly listed files.
- In general, be conservative about granting permissions related to Classloader and java.lang. For
example, granting RuntimePermission (createClassLoader) has the same effect as granting all permissions.
Following these recommendations will help ensure security in your Java applications.
Kuassi Mensah (kuassi.mensah@oracle.com) is a group product manager in the Server Technologies division at Oracle Corporation.
|