AssuredBy DesignLLC.



Notes on Java 2 Security

(Java 5 security will be added later)

1      Java 2 SE Platform Security

The major security features of the standard Java platform, also known as Java 2 Standard Edition (J2SE), includes the Java langauge itself, and the Java runtime (Java Virtual Machine, or “JVM”). Security features of so-called Java “enterprise” technologies, including servlets and EJBs, is covered in section 2.

1.1      Language Scope Rules and Their Implementation

The Java language has characteristics that make it a desirable language for building safe and secure applications. There are two reasons for this: type safety, and built-in access control.

1.1.1       Type Safety Of Java

The Java language itself defines a type-safe environment, and Java classes are “verified” before they are used, ensuring that the type-safety is not violated. The result is a safe runtime system. More specifically,

For the most part, verification occurs during class linking, and in most JVM implementations it is performed by a separate component called the “verifier”.

The upshot is significant:

  1. A large class of the most typical programming errors that result in security holes is eliminated by the verification process and by the impregnable nature of the “virtual machine” container in which a Java program is run.
  2. Programs can rely on language features to compartmentalize objects.

The traditional mechanism used in the Unix world to compartmentalize runtime objects is to put them in separate processes. Java makes it possible to keep entire object graphs safely separate, by loading them with different class loaders and simply not passing objects between them.

That said, Java has weaknesses. Some of these include:

These weaknesses become a security issue either as a result of inappropriate use, lack of safeguards against use (e.g. failure to configure a security manager or failure to adhere to certain programming best practices), or as a result of errors in a Java runtime implementation.

There are also other features of Java that could be considered weaknesses, in that stronger protections could be provided with some language changes. E.g., some [ref. 99-farias.pdf] have suggested that type compatibility constraints could be added to the language or runtime environment, so that there would be a secure way to adminjistratively manage which object types can be cast to other object types instead of merely relying on type compatibility.

No runtime system is completely secure in and of itself, and there are techniques that can be used to protect against the above weaknesses.

1.1.2       Overview Of Java Scoping Mechanisms

The Java language and runtime provide many different static and dynamic scoping mechanisms that allow an application developer to separate different parts of an application, or to separate applications from each other. Separation of different categories of memory is often referred to as “partitioning”, but I will use the term “scoping” because I feel it is more general and re-inforces the concept of establishing access scopes, based on statically-enforced or dynamically-enforced boundaries. The various scoping boundaries provided by Java are listed in the table below.

The Various Scoping Boundaries In a Java Runtime

Scope (Boundary)

Scoped (Protected) Items

VM

Threads

Stack data

Class loaders

Root class loader

System properties

Class Loader

Static class data (data = “intrinsic type data”, incl. obj. refs.

Resource data

Package

Static class data

Instance data

Classes

Resources

Class

Static class data

Instance data

Nested classes (but implementations violate this)

Method

Method (stack) data

Nested classes (but implementations violate this)

Method parameters

Code Source

Resources requiring permission (i.e. that perform checkPermission)

Note that the object heap is not mentioned in the above table. That is because Java does not actually treat objects as data: the runtime deals with object references (pointers) – not objects. Heap objects are scoped by virtue of the fact that their references are scoped. A Java VM actually contains only one logical heap (although it can contain multiple physical heaps for performance reasons), shared by all threads and class context’s running within the VM.

The Java computational unit is the thread. There is no other computational unit (e.g. process). A Java VM manages a collection of thread groups, each containing threads and possibly other thread groups. If a Java application needs to run multiple distinct logical processes within a single VM, it typically does this by creating a thread group for each logical process.

Threads can discover each other by walking up and down the thread group hierarchy, and they can communicate with each other by calling methods on each other’s threads, or by setting or manipulating thread local objects. Thus, the VM itself does not prevent communication across separate threads, and in fact provides mechanisms to achieve that communication. A security manager can be used to prevent cross-thread (or cross-thread-group) communication.

Threads execute in various contexts and frequently cross context boundaries. The ways of communicating across a context boundary include:

The Java security manager implements protection to keep many of these mechanisms from being used to inappropriately pass data across a protected context boundary, e.g. access to other thread objects in other thread groups can be regulated by a security manager. If you do not use a security manager, you do not have that protection. This is acceptable in an isolated application, but in a networked application that is vulnerable to attack running without a security manager represents a substantial risk.

The primary compartmentalization mechanism provided by the Java runtime (when used with an appropriately configured security manager) is the class loader. The protection is of the following form:

If separate class loaders are used to start separate thread groups, and as long as no shared objects are passed during the initialization of those thread groups, then those threads will not be able to communicate using classes or objects.

This compartmentalization mechanism relies on the following:

  1. There is a security manager in place that restricts access across thread groups.
  2. The system class loader and system classes (those loaded by the root class loader) do not contain variables that can be accessed by an application and used to exchange data inappropriately.
  3. Other mechanisms (e.g. data transfer via the file system or network) are not used by the application code to bypass the compartmentalization.

The third assumption relies on the safe design of the application. Object serialization is a notable way of bypassing the compartmentalization in a severe way, because it can potentially be used to construct objects with actual object references that appear to be legitimate objects, thereby circumventing the Java runtime’s built-in safeguards against pointer construction.

Some of the items in the list above have not been discussed yet, e.g. class loaders. I felt it would be better to first present a summary of the various scoping mechanisms in order to provide a top-level picture of what is going on, and then drill down into each of these. Therefore, a detailed discussion of these features is provided in sections that follow.

1.1.3       Access Control

The Java runtime system provides an access control infrastructure that applications can use to build secure resources. The infrastructure provides:

  1. Secure context, containing attributes such as user identity and code source.
  2. An access controller, that delegates to a policy implementation.

I will discuss the access control mechanisms in great depth later in this chapter, because their APIs can be used to build applications according to a resource-centric paradigm.

1.2      Resource Binding and Class Loaders

Java binds code resource names at compile time: any references to classes are elaborated into unambiguous canonical forms. It is then up to the current class loader at run time to locate the specified code resource (class).

1.2.1       Basic Operation

The mechanism that Java employs to retrieve and load code and property resources is the class loader. A Java class loader retrieves the actual bytes that compose a Java binary runtime class, loads it into memory, and links the class to the runtime. The Java runtime automatically invokes a class loader whenever a class is referenced and that class has not yet been loaded. Thus, class retrieval and loading occurs on an as-needed basis.

Most runtime systems other than Java have a single mechanism for loading executable code. Java, however, was designed with distributed operation in mind, and so its loading mechanism is extensible: Java supports the simultaneous use of multiple class loaders, arranged in a hierarchy. Each class loader may have its own mechanism for obtaining classes, e.g. from a network, from a file system, from a .ZIP or .JAR archive, etc. Applications can also install their own class loaders into the hierarchy, if they have the required permission.

A built-in “primordial” class loader forms the root of the hierarchy. The hierarchy represents an order of preference for the purpose of obtaining classes. A class loader is expected to first delegate to its parent class loader (if it has one – i.e. if it is not the root class loader) to give the parent loader a chance to find the requested class. Assuming that all class loaders adhere to this policy, it constitutes an interposition mechanism to ensure that installed resources (i.e. classes with embedded permission checks) are not bypassed and replaced by application-installed versions – that might not contain those permission checks.

Reflection On Class Loader Delegation

The policy requiring a class loader to first delegate to its parent loader is somewhat unfortunate. It presents a problem for applications that need to utilize their own versions of a shared package. For example, suppose a utility package called “com.abc” is installed within an application environment that shares a class loader, but one of the applications running in that environment needs to employ its own class loader so that it can use a newer version of that package without affecting other applications. The class loader delegation policy prevents this from working, because if “com.abc” is present in the shared class loader, that version will always be used.

The problem is that the class loading mechanism does not distinguish between classes that can be replaced and those that cannot. Classes that implement security checks should not be replaceable: they should be irrevocably bound to the resource that they protect. However, an argument can be made that other kinds of classes should be replaceable. There needs to be a mechanism similar to the keyword “final” in Java, which prevents a subclass from over-riding a method. In other words, it should be possible to designate replaceable and non-replaceable classes. Those that are replaceable should be obtained from the lowest class loader possible; whereas those that are not replaceable should be obtained from the class loader that designated the class as non-replaceable.

A class loader is a critical part of the binding infrastructure of a VM. As such, only very trusted applications or infrastructure extensions should be given permission to install their own class loaders. This requires configuration of the security manager to specify which code sources are allowed to create class loaders.

[TBD]

Figure: Example of a class loader hierarchy.

A Java VM selects a class loader beginning with the class loader of the currently executing class. A particular class loader can be explicitly specified by code, however. In that case, the calling code must have the SetContextClassLoader runtime permission.

1.2.2       Validation (Authentication) Of Security Attributes

The class loader may validate resources that it loads by verifying a digital signature and then associating that identity with the packager (assembler) of the resource archive (JAR). The network location of a resource is known as its Code Base, or “codebase”. The combination of the codebase and the signed identity is collectively known as the Code Source, or “codesource”: conceptually, it is from where and from whom a resource is obtained.

1.2.3       Manifest Package Attributes

When a class loader loads a Java class it also defines a Package object corresponding to the class’s Java language package.

In addition to an entry for each file contained in the archive, a Java 2 JAR manifest may (should) contain an entry for each Java package represented in the JAR archive. A package entry should have the following attributes:

Name—The name of the directory within the archive that contains the package’s class and resource files.

Specification-Title—The title of the API specification to which this product adheres.

Specification-Version—The version sequential number of the API specification.

Specification-Vendor—The vendor who owns the specification.

Package-Title—The Java package name.

Package-Version—The version identification of this package build; merely a string, need not be sequential in any way.

Package-Vendor—The organization that supplies this package.

Here is an example:

Name: /com/acmeapi/ourproduct/

Specification-Title: "Our Great Product"

Specification-Version: "2.0"

Specification-Vendor: "Acme API Writers, Inc."

Package-Title: "com.acmeapi.ourproduct"

Package-Version: "2.0_bugfix3"

Package-Vendor: "Show Me The Money, Inc."

These attribute assertions could theoretically be used a security attributes. The problem is that their values are strings instead of namespace identifiers. It would have been better if, e.g., the “Package-Vendor” attribute had been defined to be a distinguished name or an IANA OID.

1.2.4       Sealing a JAR File

Java 2 class loaders allow classes from a single package to be distributed across more than one JAR file. In other words, you can have some classes from package mypackage in mp1.jar, and additional classes from package mypackage in mp2.jar. You can restrict this by specifying that a JAR file or a package within a JAR file is “sealed.” If a package (or JAR file) is sealed, its packages must all originate from that JAR file.

For example, consider the manifest entry for a package called “mypackage”:

Name: mypackage/

If this entry has the additional attribute

Sealed: true

then the package “mypackage” is sealed. Of course, it is up to the class loader to enforce the sealing rule.

1.2.5       Locally Installed Application Code

A Java application can be installed as an extension to the runtime system. There is a special directory on every Java platform, known as the “extensions” directory, for this purpose. The extensions directory is part of the path (“classpath”) that the Java runtime searches for classes, so that code that is present there does not have to have its path specified explicitly for it to be found.

In addition, the policy file that is included with Java platform distributions from Sun specifies that this directory has AllPermissions, meaning that any security checks performed against its code will succeed. Of course, one could mofidy this policy simply by editing the security policy file.

This is obviously a simple extension framework, as it sidesteps the issue of trust between installed components; and does not permit multiple versions of a class to be installed, e.g. if one extension needs an older version.

1.2.6       Resolution and Loading Of Properties

…explain

1.2.7       Compile-Time Versus Run-Time Binding

Discuss how the classpath works at compile-time, assembly time, and run time.

In addition, J2EE defines various deployment mechanisms based on class loader scoping for the purpose of controlling visibility of components. As we will see in the final part of this book, these mechanisms can be used to control access between components and thereby enforce design-time scoping rules, by controlling the classpath used to build deployment units as well as the runtime classpath.

1.2.8       Discussion

Discuss how class loaders are used to secure independent application components, e.g. in an application server.

Discuss ramifications of installing your own class loader, and what you need to do to be able to do that.

Design Pattern: Use class loaders to compartmentalize resources.

1.3      Permissions and Policy

1.3.1       Permissions and Permission Collections

It is regrettable that most Java application programmers are unaware of the powerful extensibility of the Java permission framework. The system is designed so that developers can design a permission model for their application and incorporate that model into the policy authorization framework of the Java runtime. This extensibility is almost wholly untapped, as most programmers view permissions as something that must be pre-defined by the providers of the standard Java libraries, when in fact this is not the case.

A permission model is essentially the same as a capability model as described in this book. The difference is merely terminology. To define a Java permission model, one identifies the complete set of things that a resource can do and labels each of these uniquely. Using the standard Java permissions as a guide, it is customary to define a single permission type to represent an entire resource or class of resources, and to define unique “actions” that actors with the permission can perform. These actions may or may not map to the actual methods that exist in the resource’s interface, although one would expect a close correspondence.

(The Java permission framework also requires that a PermissionCollecction class be defined for each Permission class that is defined: the PermissionCollection class is designed to contain the Permission type.)

The essential concept is that a permission instance can be constructed to represent one of two things:

  1. What a user (actually, a protection domain) can do; or
  2. What a user (protection domain) wants to do.

For example, a policy store might be configured to specify that user A has FilePermission-read for a particular file. This is an example of 1. Later, if the user attempts to access a file, the file resource (e.g. java.io.File) might perform a permission check, to determine if the user in fact has FilePermission/read for the file that is being accessed. It does this by constructing a Permission object to encapsulate the precise action that the user is attempting. The policy evaluation mechanism then compares this permission object with the actual set of permissions that the user has, as stored in the policy store. Thus, this is an example of 2: the permission check that is passed for evaluation represents a desired action.

Once permissions have been defined for an application, these things must be done:

  1. Permission checks must be embedded at the application’s perimeter – i.e. at its call interface.
  2. Ensure that all internal resource accesses are encapsulated within appropriate privileged boundaries.
  3. Ensure that the application meets the best practice guidelines for a protected resource.
  4. The policy store of the target runtime must be edited, to give permission to those users (protection domains) that need access to the resource.

Step 1 in effect implements an interposition layer for the application, making it a guarded resource. This must be done with care, to ensure that all authorization checks are encoded and none are overlooked. Step 2 ensures that users of the application do not need to have inappropriate permissions to access the resource. Step 3 includes all the guidelines discussed so far for designing safe resources, including not exposing internal objects, etc. Step 4 is the last step and makes the resource available to users, by configuring user permissions for it in the Policy store, just as for any standard Java policy.

1.3.2       Policy

The Policy object is a runtime singleton component that is the endpoint of all policy permission checks that pass through the security manager. The typical sequence of calls is shown in the figure below:

SecurityManager.checkPermission(Permission)

- AccessController.checkPermission(Permission)

- AccessControlContext.checkPermission(Permission)

- ProtectionDomain.implies(Permission)

- Policy.implies(ProtectionDomain, Permission)

Figure: Sequence Of Calls During a Permission Check

As you can see, the Policy.implies(ProtectionDomain, Permission) is the method that actually makes a decision as to whether a checkPermission(Permission) call will succeed or throw an exception.

There can be only one Policy instance installed in a Java runtime. However, there is no reason that a particular Policy implementation cannot delegate some of its work to external sources, e.g. external policy “engines”. Such a delegation framework would be proprietary, however, as there is no such framework defined by the Java runtime.

A Policy object therefore encapsulates all access control decisions at a permission and protection domain level. As such, it contains a policy store – the permissions owned by each protection domain – and it contains policy logic – the logic required to decide if access should be granted, based on a protection domain’s permissions. In Java’s built-in Policy implementation this is a simple ACL decision, but in a sophisticated implementation it could be much more complex if, for example, a protection domain’s permission’s permission set were dynamically determined based on a capability aggregation model. E.g., an protection domain might be granted a permission if the domain has a sufficient set of capabilities, and if the domain has a sufficient trust level.

Java’s built-in Policy implementation is replaceable simply by calling a Java method, Policy.setPolicy(Policy). Of course, sufficient permission is needed to call this! The call in effect replaces the built-in Policy instance with the specified one.

1.3.3       Java Policy File

The built-in Java Policy object is implemented by the class sun.security.provider.PolicyFile, which extends java.security.Policy. PolicyFile is backed by a set of policy configuration files that are read at startup. The intended approach is to have a single system-wide policy file, and a policy file in the user’s home directory. The default is to have the system policy file load first, followed by the user’s policy file. If no policy file is specified, the default “sandbox” policy is used, consisting of granting no permissions.

The policy file has entries that grant permissions. Each entry consists of a “grant” statement, which contains a set of permission declarations. The grant may be unconditional, or may be conditional based on a codebase and/or a signing alias. For example:

grant signedBy "abc_customer_care",

codebase "http://customer-care.abc-company.com"

{

permission java.io.FilePermission "C:\\our_abc_orders\\*",

  "read,write";

permission java.net.SocketPermission

  "purchasing.ourcompany.com:80", "connect";

};

grants file read and write permission to the C:\our_abc_orders\ directory on a Windows system, and socket connection permission to the specified internal host. The “*” in the file permission path indicates that the permission is to be applied recursively, to all subdirectories; if it were not present, the permission would only apply to the specified file or directory. The signedBy clause in the grant statement is optional, as is the codebase clause. If neither appears, the permission is granted unconditionally. If one or both appear, the permission is granted based on whether the code is signed by the specified keystore alias and comes from the specified codebase, respectively.

PolicyFile is intended as a reference implementation, even though at this time it is essentially the only choice available. As of J2SE 1.4.1, PolicyFile still uses deprecated (obsolete) Java classes such as Identity, IdentityDatabase, and IdentityScope. It also contains some threading errors, although the static nature of the file-based policy store used by PolicyFile makes it unlikely that these threading errors will result in runtime problems.

It is possible to – in fact, it is envisioned that many application server or security product vendors will – create industrial strength replacements for PolicyFile that provide more sophisticated policy management features. The JACC specification, which is discussed later, is intended to facilitate this.

Technical Details: The Policy Files

To do:

Lay out the locations of the various property and policy files, keystores used by policy for identities, etc. (See file:///c:/tools/jdk1.4/docs/guide/security/PolicyFiles.html) Also, describe policy file keystore entry.

To do:

Explain the additional features of the syntax that I omitted above:

grant principal com.outcompany.Principal "alice" {

permission Buy "anything", signedBy "abc_customer_care";

Keystore.

Codesource principals. The principal_class_name/principal pair. Multiple codesource principals, including the policy that all principals listed in the policy grant entry must be in the domain that is being checked. The determination is based on

policy_entry_principal.implies(new Subject{domain_principals}).

where “implies” is implemented by the Principal class and is intended to essentially mean “the argument is this principal”. Thus, the principal class specified in the policy determines what implies(Subject) does – e.g. whether all principals in the domain (the Subject argument) must be listed in the grant entry, or if just one must appear.

For this to work, all principal classes must implement PrincipalComparator, which defines the implies(Principal) method, and the Principal class must have a constructor that takes a single String argument to specify the principal name.

Signed permissions.

The PermissionCollection.implies(Permission) method: In PolicyFile, it makes the final determination. (However, a customized Policy implementation could do otherwise.)

Visibility: Permission classes may not be visible when PolicyFile is loaded. The policy will therefore defer class resolution and try and resolve the permission using the class loader of the permission when a checkPermission(Permission) is performed. In fact, one could argue that all Permission and PermissionCollection class loading should be deferred so as to give an application the opportunity to provide its own class loader for loading these classes.

In the documentation for J2SE:

docs/guide/security/PolicyFiles.html

Technical Details: Predefined J2SE 1.4 Permissions

AudioPermission

AuthPermission

AWTPermission

FilePermission

NetPermission

PropertyPermission

ReflectPermission

RuntimePermission

getClassLoader

setContextClassLoader

setSecurityManager

createSecurityManager

exitVM

shutdownHooks

setFactory

setIO

modifyThread

stopThread

modifyThreadGroup

getProtectionDomain

readFileDescriptor

writeFileDescriptor

loadLibrary.{library name}

accessClassInPackage.{package name}

defineClassInPackage.{package name}

accessDeclaredMembers

queuePrintJob

SecurityPermission

SerializablePermission

SocketPermission

SQLPermission

1.4      The Access Control Model

Java’s access control framework is often described as a “sandbox model”. In fact, it is actually a resource protection model. Rather than a sandbox, it is more like a beach, and the doors to all the cabins are locked except to those who have been granted access. It is up to each cabin builder to make sure that each door has a lock, as there is no central mechanism to deploy, identify, or protect resources. There is only a central mechanism to manage context and to configure policy: it is up to applications to implement their own policy checks.

Let’s introduce some terms. The J2SE terminology for a runtime session, as defined in this book, is a “protection domain”. More precisely, a protection domain is a “space” that has identical trust attributes. This can include the current user identity, as well as the code source (source URL and signer identity) of the client code. J2SE implements a protection domain as a runtime structure that describes an entity that is accessing the runtime.

A Java access control context (ACC) is a collection of protection domains, comprising those domains that are participating in the current thread’s activity (i.e. are on the call stack). The domains are ordered according to the sequence in which they were traversed by the current thread. It is actually a little more complex than that, as we will see later, but this is the essence of it.

(Note: The Java ACC is actually an artificial entity that is constructed when needed. The Java runtime does not actually maintain an ACC, per se. Rather, it maintains an “execution environment” for each thread, and the execution environment contains a list of privileged stack frames. From these structures it is able to construct a Java ACC whenever one is needed by the Java AccessController.)

If you have knowledge of Java programming, then you know that a thread is the unit of execution granularity within a program. It is also the granularity that Java allows to have its own context. Thus, each thread has its own ACC. This makes threads very powerful for multi-user programming, because each thread’s security context is independent of other threads.

The Java AccessController is a (static) singleton component that serves as the entry point for all security policy evaluations.

The Java SecurityManager is a legacy component. It is a singleton that delegates to the AccessController. It is installable: a runtime must install a security manager if it wishes to apply policy. Otherwise, the AccessController is never called.

A Java application that needs to check security policy calls the installed security manager to request that it check if the current context has a specified permission. As explained earlier, it is primarily Java system code that makes such calls, because there is no toolkit to assist developers in making these calls or in defining the requisite permission classes. However, there is no reason why applications cannot make permission calls, in order to implement a resource-based design paradigm.

1.4.1       Privileged Boundaries

The security manager delegates permission checks to the access controller. The access controller responds by performing a policy check against each protection domain that is included in the current thread’s access control context.

There is a twist, however. The access controller provides a features that allows resource designers to implement two important design principles that are discussed in chapter 5:

The feature is that the permission check stops at a programmer-defined boundary. A programmer can define a call to be “privileged”. By doing so, the call is marked within the Java runtime, and security checks stop at the boundary. It is as if the programmer were saying, “Everything in here needs permission, but outside of here no permission is needed.” That is, the boundary defines the point at which the programmer’s code takes responsibility for the integrity of what it does beneath the privileged boundary. This is depicted in the figure below.

Figure showing how a thread, which calls many domains, determines if it can access a resource.

Let’s consider an example. Suppose you want to build a resource that provides access to a certain set of files. The resource provides methods to allow the caller to make certain business-related updates. The resource may need permission to make updates to those files. However, in most cases, the caller would not be expected to have that permission. Instead, the caller would be expected to have permission to access the resource – not the files. Thus, the resource hides access to the files, and the permissions required to access the resource are different than the permissions required to access the files.

In general, you want clients of a resource to have the minimum permissions they need to do their work; and you want the permissions to be tailored to the capabilities that reflect the nature of the functions exposed by the resource, rather than reflect internal or system-level capabilities.

The Java access control context allows this separation to be achieved, by providing the ability to define such a boundary, in the form of a privileged call.

Technical Details: Defining a Privileged Boundary

A section of code specifies that it is privileged (not really privileged, responsible) as shown in the following example:

public void someResourceMethod(...) // some service which

// requires no permission to use

{

Whatever w = null;

try

{

w = (Whatever)

(AccessController.doPrivileged(new MyAction(...args)));

}

catch (Exception ex)

{

}

class MyAction implements PrivilegedExceptionAction

{Summarized

public MyAction(...arg definitions...) {...}

public Object run() throws Exception

{

// privileged code:

...the code requiring privilege goes here...

return whatever; // can be null

}

}

}

The doPrivileged() method executes the run() method of the PrivilegedExceptionAction (or PrivilegedAction, for run() methods that don’t need to throw an exception) instance passed to it. The execution scope of the run() method is treated as a privileged section. The privileged code should be code that is algorithmically safe. That is, a non-privileged caller should not be able to misuse it. A privileged section assumes total responsibility for its actions, and so it must ensure that everything it does cannot result in harm. It is normal, therefore, for privileged code to make calls to the access controller to check security policy against the calling context.

It is extremely important that any resources that privileged code obtains, e.g., network connections, remote service connections, or local system resources, not be shared or handed or returned to (exposed to) unprivileged code! Privileged code should access its own internal resources, and then release them. If you must return a system resource, return a secure proxy for the resource.

Technical Details: How the Access Controller Checks Permission Against the Current Context

The algorithm used by the AccessController’s checkPermission() method is as follows:

beginning with the current domain n, winding back through n domains on the stack,

domain = n;

while (domain > 0)

{

if (domain does not have the specified permission)

throw AccessControlException("request denied");

else

if (domain is marked “privileged”)

return; // i.e., ok

domain--;

}

return;

In addition, the runtime needs to check permissions for any threads that we used to cre-ate the current thread. This is because when one thread creates another, the new thread inherits the complete security context of the original thread as it exists at that moment.

Thus, whatever the current thread is trying to do, the creating thread must have permission as well, or it must have performed the creation while in a privileged section. Further, this must hold for the creating thread’s creating thread, and so on, back to the original creating thread. The checkPermission algorithm above is therefore performed not only for the current thread, but for all threads along the chain of creation of the current thread, until a privileged boundary is detected, or until all domains in the current thread and all creating threads, have been checked.

Technical Details: How Context Can Be Extended

The AccessControlContext.optimize() method is critical and performs the function of combining the current context with the next outer context, be it a privileged context or an inherited thread context. This method could probably be named “combineContext” instead of “optimize” in terms of what it actually does, although conceptually it is merely performing a structural optimization of the complex nested context data.

An application programmer does not call optimize() explicitly. It is called automatically whenever one calls AccessController.getContext(). Since a programmer calls getContext() whenever a privileged context needs to be created, this ensures that any current nested context is collapsed into a single protection domain array before doPrivileged is called. The doPrivileged(, ACC) method creates a nested privileged context, which needs to be collapsed again when an authorization check is performed, so the checkPermission() method also calls optimize(). The sequence is shown below:

The getContext/doPrivileged Sequence, Both Of Which Call optimize()

acc = getContext();

acc = getStackAccessControlContext();

return acc.optimize();

doPrivileged(, acc);

checkPermission(perm);

acc = getStackAccessControlContext();

acc.optimize().checkPermission(perm);

 

So what does optimize() actually do? Essentially, it invokes the domain combiner that is attached to the ACC, if there is one. The combiner’s purpose is to fold a set of domains that it understands into the current set of domains, to produce a single array of domains that the policy’s implies() method will know how to process. Thus, whenever a new class of protection domains or a new protection domain “dimension” (i.e. a new trust attribute) is cooked up, a domain combiner needs to be defined that (1) provides a place to hang the new trust attribute type onto the ACC; and (2) knows how to apply the new trust attribute with existing domains on the stack and possibly construct new domains to that incorporate the new attribute. In addition, the default Policy implementation that comes with the standard J2SE distribution would not understand the new domain types, necessitating that it be extended or replaced with a Policy implementation that does.

Thus, the domain combiner is an extensibility feature. It provides a way for new trust attributes to be defined and incorporated into the JVM-enforced security context. This is a very powerful feature. Unfortunately it is very hard to implement and should not be attempted except by those who are very ambitious.

An Application Of Context Extension: Associating an Identity With the Current Context

Let’s consider an example: the case of the Subject security context attribute. In fact, it was the need to attach this attribute to the Java security context that introduced the need for an extension mechanism, leading to the creation of the domain combiner.

When a user authenticates, the end result is that a Subject object is created. That subject can be appended to the current security context, using the Subject.doAs(Subject, PrivilegedAction, ACC). The PrivilegedAction is the action that the application wishes to perform in the context of the specified subject. This is the call that is made internally, for example, by Netegrity’s Siteminder Weblogic “Servlet Mapper” component which propagates a Siteminder-authenticated identity to a Weblogic container.

The action of the doAs(…) call is to construct a new SubjectDomainCombiner for the specified subject, and push it onto the ACC stack by calling AccessController.doPrivileged(…) and providing the domain combiner. As we saw above, doPrivileged(…) calls optimize, which invokes the domain combiner that was pushed onto the ACC stack, causing it to combine itself (and the subject attribute) with the current ACC that is in effect. The SubjectDomainCombiner is specifically designed to know how to treat a subject attribute, and so it knows how to achieve this combination. It turns out that the SubjectDomainCombiner implementation replaces all the existing domains below the doAs(…) invocation with equivalent domains that contain the Subject attribute.

Here is an outline of what happens during a typical Subject.doAs(…) call:

doAs(Subject subject, PrivilegedAction action)

acc = AccessController.getContext();

getContext()

acc = getStackAccessControlContext();

getStackAccessControlContext()

In native code:

Construct and return an ACC with:

context = { all protection domains (PDs) up through most recent privileged frame (there are none, so context includes all PDs up through the top of the frame stack) }

isPrivileged = true, if there is a privileged frame (==false)

privilegedContext = env’s most recent privileged context (==null)

acc = optimize();

(see below)

 

SubjectDomainCombiner comb = new SubjectDomainCombiner(Subject);

acc2 = new AccessControlContext(acc, comb);

AccessController.doPrivileged(action, acc2);

Note: The javadoc for doPrivileged says,

The action is performed with the intersection of the permissions possessed by the caller's protection domain, and those possessed by the domains represented by the specified AccessControlContext.

This is correct in a general sense, but the domain combiner has the ability to modify this behavior.

doPrivileged(PrivilegedAction action, AccessControlContext acc)

In native code:

Create a privileged block, referencing acc (==acc2).

Push the privileged block onto the env’s privilege block stack.

Invoke action object’s run() method:

run()

…code from action object’s run() method…

…at some point it does something that calls checkPermission:

AccessController.checkPermission(perm);

checkPermission(Permission perm)

acc3 = getStackAccessControlContext();

getStackAccessControlContext()

In native code:

Construct and return an ACC with:

context = { all PDs up through most recent privileged frame (if any) }

isPrivileged = true, if there is a privileged frame

privilegedContext = env’s most recent privileged context (== acc2)

acc3 = acc3.optimize();

optimize()

acc4 = privilegedContext (== acc2)

if acc4 not null, and if privilegedContext has a combiner (it does: comb),

goCombiner(this.context, acc4);

Note: At this point, context consists of the most recent PDs, up through the most recent privileged frame; and acc4 was the ACC passed to the most recent doPrivileged(, ACC).

goCombiner(ProtectionDomain[] current, AccessControlContext assigned)

ProtectionDomain[] combinedPds = combine(current.clone(), assigned.context.clone());

SubjectDomainCombiner.combine( ProtectionDomain[] currentDomains, ProtectionDomain[] assignedDomains)

For each domain in currentDomains,

Add subject-based (and domain-based) permissions from Policy.

Substitute a dynamic principal-based domain.

Append assignedDomains.

Return resulting domain array.

return new AccessControlContext(combinedPds, assigned.combiner);

 

acc3.checkPermission(perm);

AccessControlContext.checkPermission(Permission perm)

Calls implies(perm) on each PD. Each must return true.

ProtectionDomain.implies(Permission perm)

PermissionCollection pc = getPermissions();

if pc.implies(perm) return true;

Policy policy = Policy.getPolicyNoCheck();

Return policy.implies(this, perm);

PolicyFile.implies(ProtectionDomain pd, Permission perm)

PermissionCollection pc = getPermissions(…, pd.getCodeSource(), pd.getPrincipals());

PolicyFile.getPermissions(…, CodeSource cs, Principal[] domain_principals)

For each policy entry,

If entry.codesource.implies(domain. codesource), AND

If every principal listed in the policy entry implies(new Subject({domain_principals})),

Add the policy permission to the return set (perms).

The permission set that met the above conditions is returned in perms. (A Permissions object is rerturned.)

Add to pc the static permissions from domain.getPermissions().

return pc.implies(perm);

Permissions.implies( Permission perm)

PermissionCollection pc = getPermissionCollection( permission);

getPermissionCollection( Permission p)

If there are “unresolved” permissions of the same type as p, attempt to resolve them using p’s class loader.

Return p.newPermissionCollection();

return pc.implies(permission);

 

 

 

 

…rest of code from action object’s run() method.

Pop the privileged block from the env.

 

Believe it or not, the above is somewhat simplified. For example, an installed Policy implementation must itself be subject to default policy checks, as implemented by java.security.Policy. Also, I have not shown the details of where codesource and permission signatures are checked, nor have I introduced into the example any inherited context from a parent thread. Finally, there is a substantial amount of caching that goes on, so that not all of this code needs to be executed all the way through with each checkPermission() call – none of that caching is shown in the example above.

Possible security hole: An application can request a permission. That permission object is used to call getPermissionCollection(). The resulting PermissionCollection is then used to call implies(Permission) to decide if the application should have access. The codesource of the application and subject of the caller are checked against the permission, but dynamic authorization rules built into the resource (via its PermissionCollection class) could be subverted by the requestor’s codebase (which is different from the resource’s – the correct Permission class’s codebase). The solution may be to enahnce the policy file to allow one to specify (bind) a code source for a permission (or better, for a resource).

NO: the permission class can be signed to prevent this. Signing binds it to an identity, and the class presented in a checkPermission must be the same class – where is that checked?

1.4.2       An Advanced Approach: Define a Domain Combiner

If resources are EJBs, this approach requires support for JACC.

Define a domain combiner.

Install a Policy object.

Perform JAAS login, using the domain combiner.

Perform checkPermission() calls in each resource.

1.4.3       Threads and Thread Groups

When one thread creates another, the new thread inherits the complete security context of the original thread as it exists at that moment. The rationale for this is to ensure that a thread’s context reflects the context in which it was created. This prevents a thread from “hiding” its context simply by creating a thread to do some subversive work: any threads it creates will also indelibly bear the context of the thread’s creator. There is no way around this: the Java runtime ensures it.

1.4.3.1   Thread Groups

The JavaDocs for ThreadGroup say,

“A thread is allowed to access information about its own thread group, but not to access information about its thread group's parent thread group or any other thread groups.”

This is incorrect. The standard security manager allows access to any thread groups except for the root thread group. Access to the root thread group is restricted to domains with RuntimePermission("modifyThreadGroup") permission.

Check this: See if I can create a pair of nested thread groups under the root thread group, and if the lowest one can modify the next one up.

Implication: It is possible for an EJB to modify other threads in its thread group. [Try this.]

1.5      Keystores and Trust

The Java class java.security.KeyStore defines the standard Java interface for a keystore. The KeyStore class is a factory interface, which allows an application to specify a particular implementation. J2SE comes with a built-in implementation, known as “JKS”. The JKS keystore is file-based: its data is stored in a password-encrypted file on the application’s computer. The implementation is not tied to a particular file, however: an application can utilize multiple keystore files, as long as they are all files that can be accessed by a KeyStore implementation such as JKS.

1.5.1       Features of KeyStore

The Java KeyStore defines a repository for public and private (asymmetric) keys as well as secret (symmetric) keys. Entries are identified by a unique name, referred to as an “alias”.

Each public key entry contains a certificate. A public key entry may also contain a private key. These entries are individually password-encrypted.

An entry may be designated as a “trusted” entry, meaning that it can serve as a trusted CA root for the purpose of validating a certificate chain. (The term used by KeyStore is “certificate entry”. This is somewhat misleading because key entries can also have certificates.)

The entire keystore is also password-encrypted.

1.5.2       “keytool”

Sun’s J2SE distribution includes a program called “keytool”. This program provides a user interface for accessing KeyStore-compliant keystores. The program has these features:

A limitation of keytool is that it does not provide for the import or export private keys generated externally. The KeyStore API does support this, however, so if this is a requirement a simple application can be written to perform this function.

1.5.3       The KeyStore Trust Model

KeyStore merely defines a simple key repository, with entries designated either as trusted or un-trusted. There is no framework for distinguishing trust chains based on their strength or other attributes, or based on domains of trust. E.g. if a certificate is validated based on a CA certificate that is trusted, there is no identification of a trust domain with associated trustworthiness. Identity trust models must be added at an application or policy level.

Since the keystore API permits the platform to have multiple keystores, it is possible to implements a policy architecture that defines a realm model or domain model such that each realm has its own keystore and each domain has distinct trust attributes associated with it. This is an intriguing idea.

1.6      Subjects, Principals, and Credentials

A principal is the base type that represents a security identity.

A subject is an object for managing a set of identities (principals) that represent a single actual entity. In the intended use of Subject, a subject has the principals that it needs to perform a particular role. That is because the normal use of Subject is to establish an ACC under the subject, and therefore ultimately the Policy implementation will evaluate access control decisions based on the set of principals owned by the subject. The default (J2SE distribution) Policy implementation allows the Principal class to determine what a sufficient or necessary set of subject principals is, by requiring the Principal class to implement PrincipalComparator and its implies(Subject) method, as described earlier. The only requirement imposed by the default policy implementation (PolicyFile) is that impies(Subject) must succeed for every principal listed in a grant entry, and the check is done one entry at a time, for each principal in the grant entry. Thus, there is no way to encode a Principal’s implies(Subject) method such that permission is granted iff all the principals in the domain are present in the grant entry. So it makes sense then to view the domain’s principal set as a collection of alternative identities – and access will be granted if all the principals in a grant entry are found in the domain’s principal set – but there may be some domainn principals left over (not listed in the grant entry).

When should a Subject be set read only? Where in the J2SE code is this used?

Note that a read-only subject does not keep one from modifying the contents of a credential. The credential must be immutable to prevent that.

PrivateCredentialPermission: the policy permission for graning access to a particular type of credential. Allows one to specify which ACCs can access credentials of a particular type. Once can also limit the permission to subjects that contain a specified principal.

There is no base type for a credential.

There is no association between a principal and a credential. Thus, if a particular principal is authenticated in some manner and is granted a credential, and then the resulting subject is subsequently authenticated as another principal using a different method, the new principal – as a member of the same subject – is indistringuishable from the first. One could not write code that expressed “only allow principals to access credentials that they obtained”. Instead, one would have to encode a policy configuration that anticipated the principals that would need access to particular credential types. Or, one could use separate subjects for each category of credentials.

javax.security.auth.x500.X500PrivateCredential

javax.security.auth.x500.X500Principal

javax.security.auth.kerberos.KerberosPrincipal

1.7      User Authentication

LoginContext

The login modules provided by Sun:

UnixLoginModule, NTLoginModule, JNDILoginModule, KeyStoreLoginModule and Krb5LoginModule in Merlin. A smartcard based JAAS login module is available from GemPlus [5].

[Provide an example of use of these, and describe what they really do.]

Callback handlers. Sun provides two. Show example of use.

2      Java 2 EE Platform Security

The Java 2 Enterprise Edition (J2EE) specification aggregates a number of important Java computing specifications, and imposes additional stringent technical requirements on their implementation, related to security, reliability, scalability, and interoperability. As such, J2EE is a specification for an enterprise computing platform, suitable for building high reliability and high volume transactional business applications that interoperate with enterprise-components within an enterprise-class infrastructure and managed by enterprise management tools. Some of the technologies included in J2EE are: J2SE, Enterprise JavaBeans (EJB), Servlets, Java Message Service (JMS), and many others.

The term “J2EE” has come to have two meanings:

  1. The J2EE specification.
  2. The independent technologies included in the J2EE specification.

For example, if one were to say, “This product is J2EE compliant”, the meaning would be according to 1 above. On the other hand, if one were to say, “We will build this using J2EE technologies”, the meaning would be according to 2, and not necessarily imply J2EE compliance. Thus, EJB is considered to be a J2EE technology because it is specifically included by adoption in the J2EE specification, even though EJB is an independent specification in its own right and in fact predates J2EE.

J2EE has been enormously successful. The J2EE technologies are among the most widely deployed on the Internet, and it is commonly the choice for the very largest and most demanding systems due to its great scalability and availability on Unix. Let’s now examine the security features of the most important technologies in the J2EE Platform.

2.1      EJB 2.0 and 2.1 (JSR 153)

The Enterprise JavaBeans (EJB) specification is the cornerstone of the various J2EE specifications. EJB defines the characteristics, APIs, and protocols for a transactional application server platform.

2.1.1       The Basic EJB Model

Enterprise JavaBeans (EJB) defines a technology architecture for a Java application platform that enables the creation of multi-user applications based on a three-tier “client-server-resource” approach. The EJB model employs remote method invocation of server objects from clients, and access of resources by the server objects. The EJB container is the conceptual server program that hosts the server objects and provides access to them. The server objects are known as Enterprise JavaBeans, or “enterprise beans”, or “EJBs” – or when the context is clear, simply “beans”. These are the business objects that application programmers develop and deploy.

EJBs interact with everything via the EJB container. The container is the application server into which the beans are deployed. The container routes client requests to the EJBs, and it routes the EJB responses back to each client. The container provides the EJBs with access to the resources that they require, e.g. databases and message queues. The container also provides the EJBs with important context information about each client request.

There are three different kinds of EJBs: (1) session beans; (2) entity beans; and (3) message beans. Further, session beans can either be “stateful” or “stateless”. The former define a user session model for holding application data across multiple user requests. The difference between these various kinds of beans is not important for our purposes here, but for the discussion that follows it is necessary to understand that these are three varieties of EJB that a programmer can create.

The programmer interface that is exposed to EJB clients is expressed as a Java “Remote Method Invocation” (RMI) interface. This is merely a programmer-level API specification, and any mechanism can in theory be used to implement the marshalling and transport functions between the client and the EJB container, as long as the mechanism used is compatible with the requirements of the container. While Sun provides a default marshalling protocol, known as Java Remote Method Protocol (JRMP), some EJB server vendors provide their own in order to provide increased efficiency or to accommodate server-specific features such as sharing of socket connections.

In addition to JRMP, the EJB specification requires that EJB containers also support the (Interoperable Inter-Orb Protocol) IIOP protocol, as an alternative to JRMP. The IIOP is an OMG specification and is the protocol used for remote method invocation of CORBA objects. The CORBA remote method invocation model is similar to RMI, and so it was possible for the OMG to enhance IIOP slightly to enable EJB servers to use IIOP instead of JRMP. IIOP is preferred for enterprise class applications because it supports the propagation of context information such as user identity and transaction ID. The interface that is exposed by an IIOP object can also be expressed in a language-neutral syntax, known as “IDL”, and therefore it is possible to program the server objects in Java but access them from clients written in other languages.

2.1.2       Scoping

Local EJBs…

An EJB can be deployed with a local interface and, or a remote interface.

Each is published with a distinct JNDI name. Therefore, a client must decide which it wants.

Can only be accessed from the same runtime – i.e. the same application server instance.

2.1.3       Context, Identity, and Runtime Binding

When an EJB receives a client request, the EJB potentially has access to three kinds of context:

  1. The Java runtime context – that is, the thread’s access control context.
  2. Explicit context, passed as programmer-defined application method parameters.
  3. Context specifically provided and managed by the EJB container.

Context type 1 is somewhat limited at present in the EJB model because neither the EJB nor the J2EE specification require that the Java runtime context reflect the client identity. In most cases (with most present EJB products) it reflects merely the codesource of the EJB, as loaded by the server’s JAR class loader. In the future this may change (see the discussion of JACC later in this book).

Context type 2 is always important to consider, and this has been discussed at length in  earlier chapters in this book. It is independent of EJB technology however, and so I will not discuss it here.

Context type 3 is supported by EJB and is provided by an API callback: Each EJB must implement a method that the container calls when the bean is invoked. This method allows the container to pass in a context object. The context object implements the interface EJBContext, which has these security-related methods:

java.security.Principal getCallerPrincipal()

boolean isCallerInRole(String roleName)

The getCallerPrincipal() method returns the identity that has been authenticated by the container. It may not return null. The principal that is returned when there is no authenticated user is up to the container; WLS returns a principal with the name “guest”.

Current specifications do not require the authenticated principal to be set in the Java thread’s ACC. In fact, in most current products (but not all), the ACC does not reflect the authenticated identity; i.e., the container does not do a doAs(Subject) when the EJB is called. The reason is that there is no point in doing so, since current EJB products do not use the Java Policy for EJB-level authorization.

The principal that is returned by getCallerPrincipal() is always the identity that is used to check access to the EJB and the EJB method that is being called. EJB supports a delegation model, known as “run-as”, that will be discussed later. When a bean is “run as” another principal, then beans that it calls will return the run-as principal when they perform a getCallerPrincipal().

EJB does not impose any rules regarding the class used to represent a principal. However, many implementations will require that it be a principal class that is defined in the realm that will be used to authorize the principal: i.e. it is typical to expect that a realm will only authorize its own principals. This is because the realm may only know how to validate the credentials of its own principals, and further it may only trust itself to authenticate principals – i.e. there is not an inter-realm trust model.

Advanced products such as IBM’s SecureWay [their realm server product] do not have this restriction. [Get more info from IBM]

The isCallerInRole(String) method tests that the current context principal belongs to the specified role. There are several things to note about this API method:

  1. Roles are specified as string values – i.e. without a namespace.
  2. The role model of the application is revealed to the EJB, potentially making the EJB dependent on the role model – i.e. on how the EJB is used.

The fact that roles are specified without a namespace means that either there must be an implied namespace, or the namespace-free names must be separately bound to a namespace. EJB provides a means to bind the role names as part of the deployment of an EJB; this will be discussed later.

The scope of the context is the current business method invocation (i.e. the current thread). That is, a business method should not save the principal obtained from getCallerPrincipal() across calls to the method, nor should it save the result of isCallerInRole(String). Instead, it should assume that these values might change each time an EJB business method is called.

On the other hand, the scope of these context values is often larger than the current method invocation context. If the EJB method invocation is a participant in a transaction context – which is usually the case – then any clients participating in the transaction must be authenticated with the same principal. The exception to this rule is that the run-as configuration of an EJB may require its calls to be made under a different identity. Also, if the EJB is a session-oriented object (a stateful session bean), the client is not allowed to change its caller identity during the session. Interestingly, however, the EJB specification does not require the container to enforce these client constancy rules.

Note: Review my comments re. roles from the perspective that an EJB role is merely a kind of principal.

Class loading: EJB Jar and J2EE application.

Technical Details

SessionContext, EntityContext, and MessageDrivenContext.

Set by the EJB container via the setSessionContext(), setEntityContext(), and setMessageDrivenContext() methods, resp.; the bean must provide for saving this value if its code needs to access it later.

“The Bean Provider can invoke the getCallerPrincipal and isCallerInRole methods only in the enterprise bean’s business methods for which the Container has a client security context, as specified in Table 2 on page 87, Table 3 on page 100, Table 4 on page 194, and Table 10 on page 273 . If they are invoked when no security context exists, they should throw the java.lang.IllegalStateEx-ception runtime exception.”

I.e., after the container has called setSessionContext(SessionContext) or setEntityContext(EntityContext). MessageDriven beans cannot call these methods.

2.1 only:

“Since the ejbTimeout method is an internal method of the bean class, it has no client security context. The Bean Provider should use the runAs deployment descriptor element to specify a security identity to be used for the invocation of methods from within the ejbTimeout method.”

javax.xml.rpc.handler.MessageContext SessionContext.getMessageContext() – 2.1 only. For obtaining JAX-RPC message context.

2.1.4       Confidentiality and Integrity

The EJB model does not constrain the marshalling or transport mechanisms used to perform remote method invocations on bean objects. The object protocols (IIOP or JRMP) are commonly wrapped in secure connections using SSL. In cases where the protocol is tunneled using HTTP, HTTPS can be used to create a secure connection between the client and server endpoints.

2.1.5       Interface and Protocol Considerations

Dynamic class loading: A security risk. (E.g. spoofing the SSL implementation.)

As of J2SE 1.4 the RMI class loader can be replaced as part of the system configuration. (See java.rmi.server. RMIClassLoaderSpi.)

As of CORBA 2.3.1, IIOP supports dynamic class loading, but it is somewhat limited in that it pertains to interface and value classes (those that are equivalent to an IDL “struct”), so this is less of an issue. Use IIOP.

Interface should distinguish explicit security context from other parameters.

Interface should not expose internal objects. In particular, do not return non-resource remote objects. Return either value objects or protected resources.

When returning value objects, do not rely on language scoping to protect against inappropriate access to fields: the object has left the VM and is therefore vulnerable to hacking.

2.1.6       The Basic Authorization Model

2.1.6.1   EJB Access

The basic EJB authorization model is an ACL model. EJB defines mechanisms for specifying access to objects and object methods based on caller identity. If the actual runtime caller identity implies the identity specified in the ACL then the access is allowed. The term “implies” can be read as “equals, belongs to, or has the role of”. For example, access can be allowed for the role “buyer”, and then any caller with an identity that is a “buyer” (as defined by the application server’s realm implementation) will be allowed access. For convenience in this discussion I will merely refer to the ACLs as containing roles, although in principle they can contain any kind of individual identity or group concept defined by the realm.

EJB roles are defined in an EJB’s deployment descriptor, and are mapped at deployment time to actual user groups, in a product-specific manner. Principals are mapped at deployment time to users or user groups, in a product-specific manner.

The EJB model accommodates programmer-designed access control by providing the context methods described earlier, getCallerPrincipal() and isCallerInRole(String). That is the extent of support for this model of access control, however, and in fact the specification discourages programmer-defined access control. It does this rightly so, from the perspective that access control frameworks are complex to construct. However, the EJB model would do well to provide such a framework. The specification writers clearly did not envision a distinction between hard-coded and potentially hap-hazard access control rules and a resource-based design that clearly delineates a permission-based access control model – the main thrust of this book.

With regard to the isCallerInRole(String) method, it should be clear to the reader by now that explicit references to roles within resource-level code is not a good thing, as it constrains the re-purposing of a resource. Therefore, use of the isCallerInRole(String) method is discouraged by this author, just as it is discouraged by the EJB specifications.

One can protect access to an entire EJB object by protecting the factory method that is used to obtain the EJB object. The method is known as the “create()” method. The factory object for an EJB is known as the “Home” object, and each Home object contains create() methods for obtaining references to EJBs of a certain type. It is possible to configure ACLs for the create() methods, just as for any EJB method, and thereby restrict access to the EJB object itself.

The actual mechanism that EJB provides to configure the ACLs of EJBs (and of Home objects) is a large-grain model suitable for defining the workflow usage of an EJB. The mechanism defines the concept of a “permission” which has no relationship whatsoever to a J2SE Permission. In fact, an EJB permission conceptually represents a workflow function, such as “submitting an order”, or “updating an account”. Generally a permission is defined for a set of EJB methods, and the user roles that will require those methods are assigned the permission. This is depicted in the figure below.

The Relationship Between Method Permissions and Roles

It is the assembler of the EJB that defines the EJB permissions. This has the effect of locking the EJB into a pre-envisioned workflow.

Critique

It would have been better if:

  • Permissions were defined at the granularity of a method or smaller, instead of at an aggregate level. Larger grain permissions could then be created if necessary, as permission collections.
  • Permissions were defined separately from the assignment of those permissions to roles. That is, roles should be defined separately, and then given permissions, using permissions that have been separately defined. This would decouple the concept of roles, which imply a workflow or usage, from permissions, which represent the capabilities of a resource (EJB).

2.1.6.2   Access To Backend Resources

The EJB model not only defines mechanisms for protecting access to EJB objects and their methods; it also defines mechanisms for protecting access to resources that the EJBs themselves access, such as database connections. Such “backend” resources are obtained using the Java Naming and Directory Interface (JNDI) API. The way it works is that a programmer (coding an EJB) specifies a logical resource name and possibly credentials (see below) to obtain a reference to a “data source” object. A data source is essentially a resource connection factory backed by a resource manager. Resource connections are normally pooled, but the resource manager makes this transparent to the EJB application. The logical resource name is mapped by the administrator to an actual resource, e.g. an actual database instance. Backend resources generally require credentials.

The EJB model permits two ways of authenticating to a resource: deployment-based authentication and programmatic authentication. Deployment-based authentication does not require the programmer to explicitly provide credentials when obtaining a resource. Rather, the responsibility of authenticating to the resource is left to the container, and it is assumed that the application server provides the administrator with proprietary mechanisms to configure an authentication and authorization policy for the actual resources. For example, an administrator might configure that certain user groups have access to a certain database resource; or specific EJBs could be granted access to the resource.

The programmatic authentication approach is simpler, but requires the programmer to explicitly pass authentication credentials when obtaining a resource connection. This is considered inferior because it forces the programmer to anticipate and possibly design an approach for managing and securing those credentials – possibly even hard-coding them in the application code.

2.1.7       Exceptions

Java uses exception objects to implement fault trapping. All exception objects derive from the base type Throwable.

The Java language makes a distinction between “runtime exceptions” and “checked exceptions”. A runtime exception is an exception that derives from the base type RuntimeException. A checked exceptions is an exception that derive from the base type Exception but are not runtime exceptions. Well-behaved programs are supposed to throw runtime exceptions in response to unexpected internal errors. This is not hard to accomplish because the Java runtime enforced the propagation of exceptions, and any internal library error will generally result in an exception of some kind.

Checked exceptions are supposed to represent anticipated conditions. The Java language enforces that code must exist to explicitly handle any checked exceptions that are thrown by the code or by any code that the code calls.

An EJB container is required to catch runtime exceptions and respond by disabling the instance and returning a checked exception of type RemoteException to the client, in some cases indicating the status of any transaction that may have been in progress. Java security exceptions are runtime exceptions, and so any Java security exceptions that occur within a bean will be trapped by the container (unless the bean code catches it). Therefore, business methods that utilize Java AccessController mechanisms as part of an application-defined security framework should catch any potential security exceptions and handle them by converting them into appropriate application level exceptions (e.g. an AccessException).

If a client is not authorized to access an EJB method, the container is required to throw a RemoteException (or its subclass, java.rmi.AccessException). (A client may also receive an EJBException or its subclass javax.ejb.AccessLocalException if the client is itself another EJB that is deployed “locally” in the same container.)

An EJB client may therefore receive RemoteException, AccessException (which is a RemoteException), and any derivative of Exception that is declared to be thrown by the EJB method. (A locally deployed client that is an EJB may also receive EJBException or AccessLocalException.) Derivatives of Exception that an EJB client might receive are also checked exceptions, and so one can be sure that the client code will consider the exception processing that is required.

2.1.8       Logging

An EJB container is required to provide logging of all runtime exceptions. It may also specially log security-related exceptions. These typically include exceptions that belong to the package java.security. Not all of these are runtime exceptions – many are checked exceptions. A securiyt log would also be expected to log all failed access attempts to resources and EJBs. The EJB specification defines no requirements with regard to the durability or security of the security log itself.

[WebLogic. WebSphere.]

2.1.9       Security Manager and Java Policy

The J2EE specification requires that J2EE-compliant containers install a security manager.

The security manager is provided by the application server vendor and enforces the constraints that EJBs must abide by, such as that an EJB cannot create threads, listen for socket connections, load native code, or directly access the file system. In general, however, there needs to be a way for an EJB designer to declare what permissions an EJB needs to perform its function. Ideally, this should be included in the assembly portion of the EJB’s deployment descriptor. This is indeed planned for a future release of J2EE.

[What is the policy file typically used with WLS? What security manager? What Policy implementation?]

Permission

Target

Action

java.uitl.PropertyPermission

*

read

java.lang.RuntimePermission

queuePrintJob

java.net.SocketPermission

*

connect

Permissions Available To EJBs

Permission

java.security.AllPermission

java.awt.AWTPermission

java.io.FilePermission

java.net.NetPermission

java.lang.reflect.ReflectPermission

java.lang.SecurityPermission

java.io.SerializablePermission

Permissions Unavailable To EJBs

2.1.10   Operating System Security Configuration

The security manager protects against the actions of EJB that run within the EJB container. It is the operating system, however, that protects the local system and infrastructure against unauthorized actions by the application server.

The EJB specification does not specify what OS-level principal should be used to run the application server process. [What does WLS require?]

What groups should the process belong to? What files, sockets, and devices does it need access to?

2.1.11   Compartmentalization

Chapter 21:

EJB persistent state: Spec requires that its security be ensured. How does WLS achieve this?

Container must allow an EJB to be deployed multiple times (each with a different JNDI name), each with a distinct security policy, and these must be able to co-exist at runtime without interference.

How does WLS configure EJB security policy at deployment?

2.1.12   Local EJBs From Independent Sources

Local EJB interfaces [describe].

Local EJB interfaces provide the following concerns:

·        Sharing of internal state across EJBs.

General rule: Only expose copies of objects – do not expose internal objects. If you pass a reference to a mutable object tree, you should assume that you no longer have ownership of that object instance tree and that the receiver now owns it.

General rule: Do not provide a remote interface for an EJB that also has a local interface, unless you carefully consider the security risks in doing so.

General rule: Do not expose local interfaces outside of a set of components that implicitly trust each other (e.g. because they are part of the same package, or belong to the same realm).

Local EJB interfaces provide the following potential benefits:

·        A scoping mechanism: Remote clients cannot access an EJB that only has a local interface.

2.1.13   Propagation Of Identity Across Containers

Review section 19.8 of EJB 2.1 spec.

How are container trust relationships configured in WLS?

Trust relationships are assumed to be transitive.

Web and EJB containers are required to support both run-as and caller propagation.

J2EE containers must support SSL 3.0 and TLS 1.0. This includes IIOP.

Security interoperability requirements of J2EE containers are based on Conformance Level 0 of the OMG Common Secure Interoperability Version 2 Specification, available from http://www.omg.org/cgi-bin/doc?ptc/2001-06-17.

Discuss SAS (from SAI.)

Interoperability is defined at the protocol level, not the interface level. Thus, the mechanism used to implement the protocol is up to the vendor, and so a mechanism constructed by one vendor is not guaranteed to work with another vendor’s product. This means that stubs and ties generated by one vendor’s IDL compiler (or EJB compiler) are not “portable”, and cannot be used, as a general rule, with another vendor’s server: each type of server must provide its own stubs and ties, based on the mechanisms that it chooses to use to implement the IIOP and SAS protocols. The practical effect of this is that EJB IIOP stubs and ties should be generated at deployment time, rather than being packaged with the component.

2.1.14   Assembly and Deployment

EJBs are normally deployed in an EJB Java archive file with the name “ejb-jar.jar”. The archive is expected to contain a deployment descriptor file called “ejb-jar.xml”. It may also contain additional deployment descriptor files for product-specific information.

Examination of bean code for illegal practices, e.g. the use of reflection.

[See how WLS prevents this.]


2.1.15   Technical Details: The Deployment Descriptor

2.1.15.1             General Layout

An ejb-jar.xml file has the basic format shown below. Not all attributes are shown: only those that are security-related are included. Annotations indicate what text is expected, and in some cases an example is provided, indicated by “e.g.” in lieu of a description.

<ejb-jar>

<enterprise-beans>

<session> (or <entity>)

<security-identity>

<use-caller-identity/>

or

<run-as>

<role-name> …role name… </role-name>

</run-as>

</security-identity>

<security-role-ref> zero or more of these

<description>...text...</description> (optional)

<role-name> (bean provider creates this, based on isCallerInRole(String) API calls.)

…name…

</role-name>

<role-link> (optional; created by assembler; link to security-role elements)

…name of a security role…

</role-link>

</security-role-ref>

<resource-ref>

<res-ref-name>e.g. jdbc/MyJDBCDataSource</res-ref-name>

<res-type>e.g. javax.sql.DataSource</res-type>

<res-auth>e.g. Container</res-auth>

<res-sharing-scope>e.g. Shareable</res-sharing-scope>

</resource-ref>

</session> (or </entity>)

</enterprise-beans>

<assembly-descriptor> optional

<security-role> zero or more of these

<description>...text...</description> optional

<role-name>

…name defining a security role…

</role-name>

</security-role>

<method-permission> zero or more of these

<description>...text...</description> optional

<role-name> one or more of these, or <unchecked/>

…name of role with this permission…

</role-name>

<method> one or more of these

<description>...text...</description> optional

<ejb-name>…name…</ejb-name>

<method-intf> optional

… Home or Remote; specifies if this method belongs to the Home or Remote interface…

</method-intf>

<method-name>…name of a method, or * ...</method-name>

<method-param> zero or more of these

…fully qualified Java type name…

</method-param>

</method>

</method-permission>

<exclude-list> zero or more of these

…methods to disable

</exclude-list>

</assembly-descriptor>

</ejb-jar>

2.1.15.2             Assembly Attribute Binding (Signing)

The J2EE Deployment API defines a Java API for deploying application components (.ear, .war, .jar, .rar) to a J2EE platform. It is a required feature of J2EE 1.4. At this time, it does not define how the deployment system should behave if an application component has an invalid signature; nor does it say anything about signatures, or about the identity of the application assembler or deployer or other important security attributes that might later be used to ascertain levels of trust.

A JAR signature does not affect the contents of the JAR: the signature is placed inside the archive in a special subdirectory, eaving the rest of the archive untouched. Re-signing the JAR results in a separate signature, also placed in the special sub-directory. The signing process does not include any existing signature(s), and therefore multiple signatures are independent of each other, and are independent of the order of signature. [Verify]

A signature “freezes” the content proper, however, and so if the content (e.g. deployment descriptor) is changed in any way, any existing signatures on that content become invalid.

The signature therefore does not impact any existing attributes, and the only information it adds is the signature itself (consisting of two files). Therefore, tools are free to ignore it. However, any tools that change the contents of the JAR invalidate any pre-existing signatures, and so if the signatures are needed for secure deployment processes, the JAR would need to be re-signed. Since the application packaging and assembly tools provided by most J2EE application development tools do not manage signatures, this means that jarsigner signing must be re-run.

At this writing Weblogic Server does not validate signatures included in JAR files. Check.

However, if a security manager is installed (J2EE requires that J2EE containers provide a security manager), and if the security policy is configured to require application components to be signed with a particular identity, then a runtime error will result if the signature is not valid. For this reason, if a security manager is used with the application server, then JAR signing must be included in the assembly process.

[Verify what WLS does if a JAR signature is invalid. If an EAR signature is invalid. If WLS detects an invalid signature, does it detect it at deployment time? Configuration time? Class load time? It is not supposed to, but verify.]

[Call Package.getPackages() and check how deployment descriptors and code source affect the values returned by the package manifest attribute methods.]

Verify which signature is used to determine the Code Source? It should be the innermost JAR containing the classes. Check.

Does an EAR signature, if present, affect the Code Source? Check.

The CodeSource is private to the class loader. Application code does not have access to it.

What is the complete class loading and extension model?

2.1.15.3             Specification Of Runtime Identity

“The security-identity element specifies whether the caller’s security identity is to be used for the execution of the methods of the enterprise bean or whether a specific run-as identity is to be used. It contains an optional description and a specification of the security identity to be used.”

Used in: session, entity, message-driven

<!ELEMENT security-identity (description?, (use-caller-identity|

run-as-specified-identity))>

The run-as-specified-identity element specifies the run-as identity to be used for the execution of the methods of an enterprise bean. It contains an optional description, and the name of a security role.

Used in: security-identity

<!ELEMENT run-as-specified-identity (description?, role-name)>

“The Application Assembler uses the security-identity deployment descriptor element for this purpose. The value of the security-identity element is either use-caller-identity or run-as. The use-caller-identity element cannot be specified for message-driven beans or for entity beans that implement the TimedObject interface.”

2.1.15.4             Specification Of Role Definitions

The bean provider can define security roles using the <security-role-ref> element. Those roles can then be referenced within the code, using the isCallerInRole().

The assembler can also define security roles using <security-role> elements. These assembler-defined roles must be mapped to the bean-provider-defined roles, by adding <role-link> elements to the <security-role-ref> definitions.

An assembler does this:

  1. Defines a set of roles.
  2. Gives method permissions to groups of roles. Those groupings are called “method-permissions”, which is highly misleading, because the permission being granted is for access to a set of methods. Further, the definition of the permission also grants that permission the specified set of roles, all in one step.

Critique

It would have been better if the assembly role-link mappings were defined separately from the bean provider’s role definitions. As currently defined, the assembler has to modify the security-role-ref sections created by the bean provider. This has two disadvantages:

  • It muddies the division of labor between the bean provider and the assembler.
  • Changes made by multiple assembly steps are not identifiable or traceable.
  • It precludes signing the JAR created by the bean provider, because any changes made by the assembler would invalidate the signature.

It would be far better if each lifecycle step added its own declarations, instead of modifying existing declarations.

2.1.15.5             Specification Of Authorization

The <method-permission> tag: Lists the bean methods which may be accessed by a specified security role. A bean method is identified by specifying its name and type signature. In addition, since a home interface may have methods that have the same name as bean business methods, the qualifier “Home” or “Remote” may be specified to distinguish which interface the method belongs to.

Unchecked and exclude-list entries.

2.1.15.6             Authentication For Resource Access

The res-auth element specifies whether the enterprise bean code signs on programmatically to the resource manager, or whether the Container will sign on to the resource manager on behalf of the bean. In the latter case, the Container uses information that is supplied by the Deployer.

The value of this element must be one of the two following:

<res-auth>Application</res-auth>

<res-auth>Container</res-auth>

<!ELEMENT res-auth (#PCDATA)>

If Container is specified, program code does not need to specify credentials in order to obtain a connection factory (data source). E.g.,

Context ic = new InitialContext(); // No credentials specified.

javax.sql.DataSource ds = (javax.sql.DataSource) (ic.lookup("java:comp/env/jdbc/MyJDBCDataSource"));

java.sql.Connection conn = ds.getConnection();

Note that when initial context is created, the environment of the EJB is used: therefore, the data source (MyJDBCDataSource) must be present in that environment: i.e. it must be defined in the deployment descriptor for the EJB that creates the InitialContext. No other EJB may use this data source definition: it is scoped to the EJB that defines it.

2.1.15.7             Deployment

“The Deployer is responsible for mapping the security roles defined by the Application Assembler to the user groups and accounts that exist in the operational environment in which the enterprise beans are deployed.”

How access control is specified at bean assembly and deployment.

The deployer maps the logical role names defined in the deployment descriptor to roles, principals or groups defined by the infrastructure such as a directory.

Scope of an EJB application, with respect to co-deployment of other applications. Access to classes or properties. Access to resources.

Class loader. Ability to set static variables.

What about thread locals? Weak references?

What about objects left over in a stateful session bean that is returned to a pool?

Scope of policy permission settings.

The security-role element contains the definition of a security role. The definition consists of an optional description of the security role, and the security role name.

Used in: assembly-descriptor

Example:

<security-role>

<description>

This role includes all employees who are authorized to access the employee service application.

</description>

<role-name>employee</role-name>

</security-role>

<!ELEMENT security-role (description?, role-name)>

The security-role-ref element contains the declaration of a security role reference in the enterprise bean’s code. The declaration consists of an optional description, the security role name used in the code, and an optional link to a defined security role. The value of the role-name element must be the String used as the parameter to the EJBContext.isCallerInRole(String roleName) method. The value of the role-link element must be the name of one of the security roles defined in the security-role elements.

Used in: entity and session

<!ELEMENT security-role-ref (description?, role-name, role-link?)>

The use-caller-identity element specifies that the caller’s security identity be used as the security identity for the execution of the enterprise bean’s methods.

Used in: security-identity

<!ELEMENT use-caller-identity EMPTY>

Clarify runAs: it actually is the identity under which a component is run. This is apart from the identity from which the component is invoked.

2.2      Servlet 2.3

Policy context

isSecure()

The container is considerably less constrained. For example, in contrast to the EJB specifications, Web applications are allowed to create threads and instantiate class loaders – as long as they have permission.

2.2.1       Scoping

From 9.6.2 Web Application Classloader:

The classloader that a container uses to load a servlet in a WAR must not allow the WAR to override JDK or Java Servlet API classes, and is recommended not to allow Servlets in the WAR visibility of the web containers implementation classes.

If a web container has a mechanism for exposing container-wide library JARs to application classloaders, it is recommended that the application classloader be implemented in such a way that classes packaged within the WAR are able to override classes residing in container-wide library JARs.

2.2.2       Context, Identity, and Binding

“If a request has been transmitted over a secure protocol, such as HTTPS, this information must be exposed via the isSecure method of the ServletRequest interface. The web container must expose the following attributes to the servlet programmer

· the cipher suite

· the bit size of the algorithm

as java objects of type String and Integer respectively. The names of the attributes must be javax.servlet.request.cipher-suite and javax.servet.request.key-size. If there is an SSL certificate associated with the request, it must be exposed by the servlet container to the servlet programmer as an array of objects of type java.security.cert.X509Certificate and accessible via a ServletRequest attribute of javax.servlet.request.X509Certificate.”

Session scope: the application (servlet context).

Static variables.

Instance variables.

Thread locals.

Weak references.

“If a web container has a mechanism for exposing container-wide library JARs to application classloaders, it is recommended that the application classloader be implemented in such a way that classes packaged within the WAR are able to override classes residing in container-wide library JARs.” – Means that extensions should be viewed as unsecure, because there is no way to ensure that they are used.

By default, a servlet propagates the authenticated caller identity to an EJB. If there is no caller identity, a pseudo identity can be used: e.g. WebLogic uses the user “guest”. Alternatively, a WAR may specify a “run-as” identity for EJBs: Any EJB calls then must be made with the specified run-as identity (role).

2.2.2.1   Session binding mechanisms

Session implemented with any of these:

  1. Cookie-based token.
  2. URL-based token.
  3. Binding to the security context of an SSL session. (The SSL session is not required to employ client authentication.)

“…a servlet container is required to track authentication information at the container level and not at the web application level allowing a user who is authenticated against one web application to access any other resource managed by the container which is restricted to the same security identity.”

The two apis (old and new) for getting the caller identity.

WLS does a doAs(Subject) based on the caller identity.

2.2.2.2   Logon mechanisms

·        Form-Based

Form-based login:

<form method="POST" action="j_security_check">

<input type="text" name="j_username">

<input type="password" name="j_password">

…other tags that personalize or brand the form…

</form>

The container is expected to intercept a POST with the “j_security_check” action and process it as an authentication request.

2.2.3       Confidentiality and Integrity

Container must provide optional data confidentiality and data integrity at the transport level.

2.2.4       The Basic Authorization Model

“By default, authentication is not needed to access resources. Authentication is only needed for requests in a specific web resource collection when specified by the deployment descriptor.”

From 12.2: “The security model does not intervene between a Servlet using the RequestDispatcher to invoke a static resource or Servlet and the static resource or servlet being requested by a forward() or an include().” I.e., forwarding bypasses the servlet authorization model.

Authorization rules are specified as “security constraints”. A security constraint is a set of URLs and a set of roles that may access those URLs. Thus, it is purely a role-based model. It actually allows for a little more precision: one can specify HTTP request methods (e.g. GET, POST, HEAD…) for the URLs, and whether confidentiality and integrity (e.g. via HTTPS) are required for the connection.

2.2.4.1   Conatainer-Enforced

Role-based.

Resources defined by URL patterns and role-based ACLs. When matching URL patterns, “first match wins”.

2.2.4.2   Programmatic

HttpServletRequest:

getRemoteUser() (legacy method?)

isUserInRole(String)

getUserPrincipal()

“If getRemoteUser returns null (which means that no user has been authenticated), the isUserInRole method will always return false and the getUserPrincipal will always return null.”

2.2.5       Delegation

2.2.6       Exceptions

[What are the exceptions or responses that are generated when an authentication failure occurs? When an authorization failure occurs? When an EJB authentication failure occurs? When an EJB authorization failure occurs?]

2.2.7       Logging

2.2.8       Security Manager and Java Policy

Is a security manager required?

What happens to permission exceptions?

2.2.9       Deployment

A Java Web application is packaged for deployment in a JAR file, with a .war extension, and for this reason is often referred to as a “WAR” file. A WAR file contains a deployment descriptor file, with the name “web.xml”.

Technical Details: The Deployment Descriptor

2.2.9.1   General Layout

A web.xml file has the basic format shown below. Only security-related attributes are shown. Annotations indicate what text is expected, and in some cases an example is provided, indicated by “e.g.” in lieu of a description.

<web-app>

<security-role> zero or more of these

<role-name> …role name, as seen by administrator… </role-name>

</security-role>

<servlet>

<security-role-ref> zero or more of these

<role-name>AnyUser</role-name>

<role-link>any_user</role-link>

</security-role-ref>

</servlet>

<servlet-mapping>

<servlet-name>e.g. My Servlet</servlet-name>

<url-pattern>e.g. /myservlet/*</url-pattern>

</servlet-mapping>

<security-constraint>  zero or more of these

<web-resource-collection> one or more of these

<web-resource-name>e.g. ServletData</web-resource-name>

<url-pattern>e.g. /static_data/*</url-pattern>

<url-pattern>e.g. /myservlet/*</url-pattern>

<http-method> e.g. GET</http-method>

<http-method> e.g. POST</http-method>

<http-method> e.g. HEAD</http-method>

</web-resource-collection>

<auth-constraint> 0 or one of these

<role-name>e.g. any_user</role-name>

</auth-constraint>

<user-data-constraint> zero or one of these

<transport-guarantee>

value may be one of: { NONE, INTEGRAL, CONFIDENTIAL }

</transport-guarantee>

</user-data-constraint>

</security-constraint>

</web-app>

2.2.10   Propagation Of Identity Across Containers

By default, the caller’s identity is propagated. However, can specify “run-as role” in the deployment descriptor’s <ejb-ref> element so that calls to EJBs from the servlet will be performed as the specified role.

The role must be declared in the web application’s deployment descriptor. However, the EJB may be in another J2EE application, if any. This implies that the role that is propagated to the EJB component must also be defined for that component. I.e. the scope of a role can bridge multiple applications, as long as it is defined in each application that uses the role.

Try this out.

Issue: The compartmentalization of roles.

2.3      J2EE 1.3 and 1.4

A goal expressed by the specification:

“Transparency: Application Component Providers should not have to know anything about security to write an application.”

This worthy goal is not achievable today. Developers need to know about best practices with regard to security, and in fact must select and adhere to secure design patterns.

Concept of “security attributes” – attributes associated with a principal.

2.3.1       Assembly and Deployment

J2EE Section 6.2.2.

6.2.4.6.

6.2.4.8.

6.2.4.10.

Do not package untrusted components within the same Enterprise Application.

The application element is the root element of a J2EE application deployment descriptor.

<!ELEMENT application (icon?, display-name, description?, module+, security-role*)>

The role-name element contains the name of a security role. The name must conform to the lexical rules for an NMTOKEN.

Used in: security-role

<!ELEMENT role-name (#PCDATA)>

The security-role element contains the definition of a security role. The definition consists of an optional description of the security role, and the security role name.

Used in: application

Example:

<security-role>

<description>

This role includes all employees who are authorized to access the employee service application.

</description>

<role-name>employee</role-name>

</security-role>

<!ELEMENT security-role (description?, role-name)>

In the .ear file, reconcile security roles. (See 8.3.1.)

2.3.2       Class Loading

One EAR loader per EAR. The EJBs are loaded by this.

One loader per WAR in an EAR.

The EAR loader is the parent of the WAR loaders.

EJB classes therefore cannot be over-ridden.

In the previous chapter, in the section Resource Binding and Class Loaders, it was pointed out that a class loader is expected to always first delegate to its parent; i.e. a class loader should load a class itself only if its parent loader is unable to.

Classes can be added to the EAR classpath by listing them in the Class-Path attribute of a WAR (or EJB) manifest. [Is this WLS-specific?] However, this has the effect of tying your components to a particular usage. Ideally components should not have to be modified to reflect their usage. [Is there a way to configure the EAR so that it specifies a classpath?]

From J2EE.6.2.4.8 Context Class Loader:

This specification requires that J2EE containers provide a per thread context class loader for the use of system or library classes in dynamicly loading classes provided by the application. The EJB specification requires that all EJB client containers provide a per thread context class loader for dynamicly loading system value classes. The per thread context class loader is accessed using the Thread method getContextClassLoader.

The classes used by an application will typically be loaded by a hierarchy of class loaders. There may be a top level application class loader, an extension class loader, and so on, down to a system class loader. The top level application class loader delegates to the lower class loaders as needed. Classes loaded by lower class loaders, such as portable EJB system value classes, need to be able to discover the top level application class loader used to dynamicly load application classes.

We require that containers provide a per thread context class loader that can

2.3.2.1   Security Manager

The J2EE specification requires that J2EE-compliant containers install a security manager. It also specifies a list of permissions that components can expect that they will have. An application component cannot assume that it will have any additional permissions, unless it can assume that the administrator will reconfigure the container’s JVM security policy. An administrator is permitted to change the permission set, but is aware that some components might be affected by doing so.

The permission set from Table 6.2 of the Java 2 Platform Enterprise Edition Specification, v. 1.4, is repeated below:

Permission

Target

Action

java.lang.RuntimePermission

queuePrintJob

java.net.SocketPermission

*

connect

java.util.PropertyPermission

*

read

Default Permissions Available To Applications In J2EE-Compliant EJB Containers

Permission

Target

Action

java.lang.RuntimePermission

queuePrintJob

java.net.SocketPermission

*

connect

java.util.PropertyPermission

*

read

java.lang.RuntimePermission

loadLibrary

java.io.FilePermission

*

read,write

Default Permissions Available To Applications In J2EE-Compliant Servlet Containers

Permission

Target

Action

java.awt.AWTPermission

accessClipboard

java.awt.AWTPermission

accessEventQueue

java.awt.AWTPermission

showWindowWithoutWarningBanner

java.lang.RuntimePermission

exitVM

java.lang.RuntimePermission

loadLibrary

java.lang.RuntimePermission

queuePrintJob

java.net.SocketPermission

*

connect

java.net.SocketPermission

localhost:1024-

accept,listen

java.io.FilePermission

*

read,write

java.util.PropertyPermission

*

read

Permissions Available To Applications In J2EE-Compliant Application Client Containers

Permission

Target

Action

java.net.SocketPermission

codebase

connect

java.util.PropertyPermission

limited

read

Default Permissions Available To Applets In J2EE-Compliant Applet Containers

These permission impose additional access constraints on top of the access constraints imposed by the underlying native resources.

2.4      Java Authorization Contract for Containers (JACC)

2.4.1       Overview

We have seen that the Java Policy framework is not utilized by J2EE to implement the authorization models that are defined by the various J2EE specifications such as servlets and EJB. As a result, there are currently two entirely separate authorization frameworks within every J2EE implementation: the framework defined by the particular J2EE technology, and the lower level Java Policy framework that is used to check permission for access to low level resources such as the file system. The objective of the Java Authorization Contract for Containers (JACC) is to bring these disjoint worlds together, by defining a standard way that a Java Policy implementation can be utilized to implement J2EE authorization.

As we have also seen, J2EE authorization is declarative and ACL/role-based: deployment descriptors specify which roles are allowed to access which resources and methods. There is no provision for decision logic. The same is true of the default implementation of Java Policy. However, Java Policy is an extensible framework that can be enhanced to provide arbitrarily advanced authorization decision-making.

It is possible to leverage Java Policy to implement J2EE. In doing so, issues arise as to how to represent J2EE ACLs in terms of permissions. Issues also arise as to how to identify the individual J2EE application components to policy stores. These are the issues that JACC addresses.

2.4.2       Policy Installation

Delegation to default policy

2.4.3       Deploying a J2EE Component

When a component is deployed the security rules expressed by its deployment descriptor must be imported into the policy store. Note that a deployment descriptor essentially contains two kinds of data with regard to authorization: resource declarations and the roles that are allowed to access those resources. For example, the <method-permission> element in an EJB deployment descriptor lists (1) particular EJB methods and (2) the roles that are allowed to access those methods.

JACC specifies that when a component is deployed to an application server, the application server must define permission objects to represent the resources that are listed in the component’s deployment descriptor. These permission objects are then passed in calls from the container to the policy store, to convey the access control restrictions that are expressed in the deployment descriptor. Thus, JACC defines policy methods such as addToRole(role, permission) to allow the container to inform that policy store that the specified role has the specified permission.

Technical Details

JACC defines five permission classes that must be used by a JACC-compliant application server to translate the EJB and Web resource declarations in deployment descriptors into equivalent permissions. These are:

  • EJBMethodPermission
  • EJBRoleRefPermission
  • WebResourcePermission
  • WebRoleRefPermission
  • WebUserDataPermission

For example, the EJBMethodPermission constructor accepts four arguments: the EJB name, the EJB method name, the method interface class name, and a list of the method arguments. This information uniquely identifies the EJB and method within the application deployment environment. Once the application server container translates the deployment descriptor’s EJB method and Web resource references into equivalent permissions, the container uses these permission objects in calls to the policy configuration for the J2EE application component.

The mapping between a J2EE deployment descriptor and the JACC permission model is not one-to-one. A J2EE deployment descriptor defines a collection of sets of roles that have sets of permissions (see figure …), whereas the JACC model is much simpler and defines a collection of roles that have permissions, as shown in the figure below.

 

The JACC Permission Model

The J2EE model is transformable into the JACC model because the former contains more information. That information is lost in the transformation however and so a deployment descriptor’s access control specification cannot be derived from the information that is given to a policy object via its JACC interface. This is not a problem because the J2EE model’s complexity exists only to facilitate description and provide convenience for the component assembler and serves no runtime purpose – i.e. the policy object doesn’t need it.

JACC also defines that the policy implementation must be able to maintain a distinct “policy configuration” for each J2EE deployable unit, i.e. for each EJB JAR and each Web application (WAR). This is necessary to accommodate the fact that J2EE requires that independent components be able to be independently administered. Thus, when permissions are added to a role, the additions are applied to the policy configuration for the J2EE resource in question. E.g., the call addToRole(role, permission) would be made on the policy configuration for the component instance that the role is to have access to.

The overall sequence of events during deployment is shown in the figure below. In step 1, a J2EE component (e.g. and EJB JAR, WAR file, or EAR file) is provided to the application server and the deployment descriptors within are retreived and parsed. In step 2 permission objects are constructed to represent the resources elements that are being deployed that need protection. In step 3 the application server container obtains a policy configuration for the newly deployed component; in doing so, the contianer provides a unique identifier that will henceforth be used to identify the configuration. Generally, this identifier is the path of the component in the application server’s resource namespace. Finally, in step 4 the container makes a series of calls to the configuration, to convey the deployment descriptor’s role access specifications.

Deploying a J2EE Component

2.4.4       J2EE Context

JACC requires the container to create an appropriate ACC representing the identity of the caller, or the “run-as” identity for an EJB, and invoke the J2EE resource (EJB, servlet, etc.) within this context. In other words, the container creates a subject-based domain by performing a doAs(Subject, ACC) as described earlier in the discussion of J2SE. The ACC created in this way does not contain or reference any J2EE-specific attributes; it is merely required to contain the principals of the caller.

JACC augments this mechanism by providing a separate mechanism for the container to attach other kinds of context data to the current thread. The policy implementation is able to subsequently retrieve that additional context information for use in an authorization decision. The context data that is attached by the container to the thread varies depending on the kind of container, i.e. depending on whether it is an EJB container, a servlet container, and so on.

The policy implementation can request the context from the container by making the getContext(kind) request to a JACC layer known as the PolicyContext, which extracts the data that the container has attached to the thread, forwards it to the a container-supplied handler for formatting or elaboration, and then returns it to the policy implementation. In the getContext(kind) request, the “kind” parameter (it is not actually called that – I am using that name to clarify its purpose) is used to specify the kind of context requested, e.g. “SOAPMessage” or “EnterpriseBean” (these are abbreviated for this discussion).

The reason that the policy has to specify the kind is because some types of container can provide more than one kind of context. E.g. all containers can provide a subject context, indicating the identity of the current caller (this is redundant with respect to the ACC), but they can also provide context data that is unique to the container type, e.g. an EJB container can also provide the “EnterpriseBean” context and the “EJB.arguments” context. The intent is to make the full calling context available, and to allow the policy to request specifically what it needs for the purpose of making an authorization decision. Depending on the type of container, the five different pre-defined kinds of context that are available by calling getContext(kind) are:

The container is expected to provide a callback handler for each of the kinds of context that it supports. It does so, normally at startup, by calling the JACC method registerHandler(kind, handler). This process is shown in the figure below. In step 1, the container creates a handler for handling callback requests for context. In step 2 the container registers the handler with JACC by calling the static method registerHandler(kind, handler).

Container Initialization: Registering a Context Type For Retrieval By Policy

It is important to understand that the identity used to check for access to a J2EE resource such as an EJB is potentially different from the identity used to invoke the resource. For example, if an EJB specifies a run-as identity, the run-as identity is used to actually invoke the resource method. In that case, the method executes as the run-as identity: an ACC is created with that identity, and:

2.4.5       Sequence Of Events During a J2EE Component Invocation

A policy implementation interacts with JACC during a normal J2SE permission evaluation. As we saw in the earlier discussion of J2SE, a security manager checkPermission(Permission) method delegates to the AccessController, which in turn ultimately delegates to the policy implementation by calling its implies(Permission) method. It is assumed that when this call occurs, that any required ACC has been attached to the current thread via a doPrivileged(…) call; otherwise, the implies(Permission) check will fail.

When the policy implemetation receives an implies(Permission) call, it is permitted to perform whatever logic it chooses to evaluate the request. This is the job of policy: to make access control decisions. The J2SE uses the ACC mechanism to attach context information to the current thread. JACC provides a separate mechanism: the getContext(kind) API method.

The JACC specification allows a container to decide how it invokes the policy implementation. It identifies the “normal” pathway: the invocation of a security manager checkPermission(…) method, but it also identifies other possible pathways, including direct direct invocation of the Java Access Controller, and even direct invocation of the policy’s implies(Permission) method. Regardless, the policy must then obtain the calling context in order to make a decision.

The typical sequence of events in a J2EE resource invocation is shown in the figure below. It begins with the receipt of a service request, e.g. an EJB method call. The container begins to service the request by establishing the baseline context of the request: the caller’s authenticated identity. It does this by performing a Subject.doAs(…), as depicted in step 1. The subject passed in was presumably obtained via a login process.

In step 2 the container obtains the policy configuration for the J2EE application that is being accessed. It does this by specifying the resource name, e.g. the deployed name of the EJB. In step 3 the container uses the resource’s policy configuration to obtain the Id of the policy configuration. (This ID may be identical to the resource name, but that is an implementation choice for the policy implementation.) The container then (step 4) utilizes the JACC setContextId(id) method to associate the configuration Id with the current thread. This association will only exist for the scope of the current resource invocation.

In step 5 the container provides JACC with a handle to data that will be used later to obtain context data (such as the EJB arguments for an EJB call). JACC associates the datas with the current thread.

In step 6 the container initiates a policy access control check. It can do this in any number of ways. The most typical way is to make a J2SE security manager call to checkPermission(Permission), providing a permission object that represents the request. This would be one of the permission types defined by JACC, described earlier. The checkPermission(…) method delegates to the Java AccessController, and eventually finds its way to the policy’s implies(Permission) method. The policy implementation then is expected to honor the role permissions specified in the resource’s deployment descriptor that were imported into the policy, as described earlier. This includes first checking excluded permissions (resources), then the unchecked permissions (resources).

If the resource permission is not in the excluded or unchecked list, the policy is free to determine if access should be granted based on its own criteria, which may include the role-based access rules specified in the deployment descriptor, as well as the JACC context, which may include call arguments. The way in which JACC policy is managed and integrated with container-provided deployment access control configuration is not specified by JACC.

Accessing a J2EE Component

isCallerInRole(role) must be implemented by a [EJB/Web]RoleRefPermission. [See 4.3.2]

Critique: Unnecessary Differences Between J2SE and JACC

J2SE uses DomainCombiner to attach context data to the thread. JACC uses DomainCombiner for J2SE data but uses setHandlerData(data) for other context data.

J2SE uses ACC to retreive context data. JACC can use ACC for J2SE data, but uses getContext(kind) for other context data.

Critique: Why does JACC not use the DomainCombiner and ACC mechanisms for other context data? – that is what the DomainCombiner is for.

Critique: It is not clear how or if deployment descriptor role-based resource ACLs (e.g. the EJB <method-permission> elements) are to be used by policy, nor is it clear how the container should interace with Policy when configuring deployment descriptor resource ACLs.

2.4.6       Observations

How application-derived permissions could fit into this scheme.




Creative CommonsAttributionThis page is licensed under a Creative Commons License.