OneStopTesting.com - Testing EBooks, Tutorials, Articles, Jobs, Training Institutes etc.
OneStopGate.com - Gate EBooks, Tutorials, Articles, FAQs, Jobs, Training Institutes etc.
OneStopMBA.com - MBA EBooks, Tutorials, Articles, FAQs, Jobs, Training Institutes etc.
OneStopIAS.com - IAS EBooks, Tutorials, Articles, FAQs, Jobs, Training Institutes etc.
OneStopSAP.com - SAP EBooks, Tutorials, Articles, FAQs, Jobs, Training Institutes etc.
OneStopGRE.com - of GRE EBooks, Tutorials, Articles, FAQs, Jobs, Training Institutes etc.
Pitfalls of the Java Permissions Model | Articles | Recent Articles | News Article | Interesting Articles | Technology Articles | Articles On Education | Articles On Corporate | Company Articles | College Articles | Articles on Recession
Home » Articles » Pitfalls of the Java Permissions Model
Pitfalls of the Java Permissions Model
Article Posted On Date : Wednesday, May 20, 2009
Pitfalls of the Java Permissions Model
Advertisements
HTML clipboard The Java SE Access Control model, built around various permission classes and code-based security, has not been able to grow up with the Java platform and cannot satisfy the requirements of today's enterprise systems. This article analyzes the root causes of the problem, and suggests alternative approaches.
Brief Overview of the Java SE Model The model is based on using permission classes from the code to assert the right to perform some action. When certain action is about to be executed in a particular class/page/etc., the code invokes SecurityManager
's (or AccessController
's) method checkPermission(Permission)
, passing it an instance of some class derived from the base Permission
class. The AccessController
is then responsible for iterating over the call stack, verifying that each frame on the stack contains the desired permission. Figure 1 shows this arrangement.
Figure 1. Stack walk during checkPermission
call It is important to understand that in this model it is the permission class itself that is responsible for executing the evaluation logic, as well as defining the resource structure and available privileges. Permissions for each code frame are derived from those of the executing Java Principal
(s) for the current thread and its CodeSource
. They are calculated by the installed Policy
provider, for each code frame on the stack, and the provider is responsible for invoking evaluation logic for each permission class in the resultant set to determine whether the sought permission is implied in it.
When successful, the overall checkPermission(Permission)
call either simply returns, or otherwise throws a SecurityException
to indicate a security violation.
History Behind the Java SE Model Java's security model traces its roots to the early days of the platform, as it was mainly viewed as a browser extension mechanism for enhancing user experience. The Java code for execution would be derived from various sources, some of them of unknown or untrustworthy origins. Correspondingly, the main focus of the platform's security was initially aimed at solving the problem of verifying that the executing code can be trusted, and the entire game revolved around execution of applets in browsers. However, the simple division into trusted
and untrusted
universes that this model dictated was not sufficient for running even moderately complicated applications.
Starting with the 1.2 release, as Java was getting accepted as a programming platform and not simply a browser extension, Sun began introducing more flexible security features, the first being the notion of a configurable security policy. Its evolution is covered in the official Java documentation
.
When the Java platform started making inroads into enterprise environments, it quickly became obvious that purely code-based features are not sufficient to manage security in large-scale applications. Version 1.4 of the Java platform introduced a new feature, called Java Authentication and Authorization Service (JAAS)
, for incorporating user-based permission entries into the security policy. Now, the permissions for a particular code frame on the stack are based both on the code's origin (its CodeSource
), as well as the user's identity, groups, and roles, assigned to him during authentication.
Today's Problems Even after such a long evolution, Java's security model is still best-suited for code access security. However, this model simply fails to address the needs of higher-level enterprise applications, which demand many more access control features than simple permissions checks are capable of performing. Some of these problems are reviewed in the sections below.
Management Functionality Arguably, when it comes to managing security in general and access control in particular, the single greatest obstacle is the absence of a straightforward, yet powerful management model. The list below is intended to highlight the most obvious shortcomings of Java's permission-based model.
Permissions Proliferation The Java platform comes with a number of standard permissions, which are invoked by its own code (see Permission
, and its subclasses
). However, those permissions are not applicable for an application that tries to protect its own components, so the application has to define its own classes, and quite a few of them.For example, consider a page of a workflow application, as seen in Figure 2:
Figure 2. UI screen for access control Not counting standard SE and EE permissions, which are invoked by the application and container (but still need to be managed in the policy), this page would need to define a few separate permissions. First, the application needs to define a specialized permission (call it TransferPermission
) to verify that the user is authorized to perform this step of the workflow process. Second, it checks whether the user can see and use text boxes and buttons on this screen, which would require several additional UI permissions (call them AccountEditButtonUIPermission
, AccountTextUIPermission
, and AmountTextUIPermission
). Third, some of the data on the page, such as the account number, might need to be hidden; unfortunately, Java does not provide any mechanism to address that need (see the Consistency of Data and Function Security section).
Considering that large-scale applications (especially in the financial and healthcare industries) typically have thousands of elements, this quickly pushes the total number of defined permissions to hundreds or even thousands, making the entire set simply unmanageable.
Resource Model Each permission, regardless of whether it is standard or custom, incorporates its own resource interpretation logic into the evaluation process. Which means that permission classes operate on different representations of application resources, making it impossible for security administrators to create a coherent application management picture of the type shown in Figure 3.
Figure 3. Resources hierarchy For instance, consider Java's standard FilePermission
and SocketPermission
. The former operates on resource strings, representing files and directories, in the format "/MyDir1/MyDir2/*"
, while the latter uses hosts, sockets, and ports: "localhost:1024-"
. In the general case, such differences make it impossible for any administration application to parse the resource strings used by arbitrary permission classes in an application.
Policy Hierarchies Closely related to the resource model is the absence of policy hierarchies in the Java permission model. A policy hierarchy means that policies, defined at a higher level in the application's resource tree, are applicable to the lower nodes as well, according to well-defined combining algorithms. The advantage here is that a security administrator can define access control policies at the higher levels in hierarchy, and they will be applicable to all applications/domains under his command.
Considering the hierarchy from Figure 3, the administrator could restrict access of traders to the trading application, as well as which actions that they can perform, for certain periods of time, in accordance with the company's policy. The permission-based model simply does not allow this level of control, since it does not have a notion of resource hierarchies, and its only policy-combining mechanism consists in creating a union of applicable grant entries from all policies.
Policy Elements Another important aspect of access control is in the capabilities of the security policy itself. Java's policy is very primitive -- it consists of permission sets in grant entries
, assigned by Principals
, code origin, or code signer (combined into CodeSource
).
This model lacks the following very important features of authorization policy:
An Ability to Configure DENY
Policies Java treats the absence of an explicit GRANT
as an implicit DENY
. However, given the absence of a uniform resource model, in order to deny access to a particular resource for a user or group, a security administrator has to go through all of the grant entries in the policy to verify that none of them grants access. In the general case, this is an open-ended task, which has to be repeated upon each policy modification. A much easier alternative would be defining a DENY
policy at the top of the application hierarchy, and specifying that a DENY
result takes precedence.
An Ability to Configure Attribute-Based Constraints The policy itself specifies that a particular user (member of some groups and in certain roles), or code (of particular origin or signature) has certain privileges on a particular resource (for instance, FilePermission("/app/user/US/MA/*", "read")
assigns the privilege read
to the resource "/app/user/US/MA/*"
). This means that this file permission would have to be granted to all Massachusetts-based users, groups, or roles that require access to that area, and removed when they do not need it any longer (for example, if they moved to a New York-based office). The same goes for California-based employees, and so on.
It is the attribute-based constraints (aka "conditions") that provide real flexibility in applying the authorization policies. Those constraints allow including external metadata to control when and how a policy's decision can be applied. The real flexibility comes from where and how this metadata can be fetched from: directories (such as LDAP), environment (for example, the OS), or provided with the actual request to the application.
Going back to the earlier example, a single attribute-based policy, applicable to all users and locations across the company, should be defined instead of granting file permissions to multiple user groups:
GRANT {User, Resource, "read"} IF (User.location IN Resource.location)
An Ability to Return Obligations to the Caller Oftentimes, it is insufficient for the application to know whether access is granted or denied, as it needs to know additional information that comes with the decision. For example, the policy may specify a broad GRANT
, but also contain restrictions for some fields, which need to be communicated back to the calling application (aka Policy Enforcement Point
, or PEP
). Such return data are called obligations, and provide an important extensibility mechanism for extending application functionality without the need to hardcode security logic. Java security policy does not provide such a capability at all.
Governance Governance tasks are intended to answer "who can do what, under which conditions" for audit purposes. It is a pretty hard problem to solve in the general case, but Java's permissions-based model makes it close to impossible for any large-scale deployment. Since permissions themselves own the task of interpreting the resources, an external tool needs to take into account all possible permissions used by an arbitrary application in order to understand which application resources are protected and how. Furthermore, such a tool will have to be updated with each revision of the application. Clearly, this presents a maintenance nightmare and does not allow for development of a generic auditing tool for such purposes.
Consistency of Data and Function Security Last, but not least, Java's security model is all about functional security; i.e., it is designed to answer questions about access to particular code functionality. However, in many applications it is equally important to provide a consistent view of the data, redacted according to the user rights. See the section Permissions Proliferation for an example of where and how this functionality could be applied.
An alternative, available in Java, usually requires either developing an additional mechanism (for example, via tricks with database queries), or displaying the entire list but then performing access control at the moment when the user tries to access a particular record. The first alternative (having two models) is simply unmanageable and will lead to inconsistencies. The second (performing checks at access time only) reveals too much information, and results in poor usability when a user is shown records, but has no way of accessing them.
Runtime Functionality Runtime experience presents another half of the problem, and Java's model again falls short here for the reasons explained below.
Unnecessary Stack Walks As has been repeatedly pointed out before, Java's model is intended for code-based security. Correspondingly, upon each request to checkPermission(Permission)
, a stack walk
is triggered to determine whether all code frames on the stack have the desired permission.
However, security policies for large-scale applications care not about the code, but about user checks. In practice, that means that each call to check user privileges turns into an unnecessary stack walk. Moreover, this works at all only as long as the policy processing code (called a Policy Decision Point , or PDP) is co-located in the same process with the application. Due to the hard-coded evaluation logic of AccessController
(see also Extensibility of Evaluation Logic), any attempt to externalize the PDP will result in multiple remote calls for each checkPermission(Permission)
invocation, rendering the entire system unusable.
Extensibility of Evaluation Logic The logic of Java's security model can be changed only in a single way: by replacing the security policy provider
. This restriction makes it impossible to customize the stack evaluation algorithm, hard-coded in the AccessController
, to make it more efficient for remote calls, for example, or to optimize it for user-based access control checks.
Bulk Authorization Bulk authorization really helps to improve application performance when multiple elements of a single component need to be authorized, by wrapping multiple requests into a single call to PDP. For example, consider the figure of a web page in the Management Functionality section. This page has to make multiple requests to verify all required elements on the page. However, it would be much more efficient to package them together and make just a single call. Unfortunately, once again, Java does not provide any support here.
Similar to the bulk authorization is the issue of querying access , which means that the application asks "which resources can a particular user access under a certain resource node, with the given action and attributes?" Depending on the returned result, the application then goes over the child resources, taking specific actions on them. For example, a web page may inquire at load time which controls are accessible to this user, and then hide/disable those that are not permitted by the policy. Java, due to its lack of a single resource model, cannot determine applicable permissions and, therefore, does not have such functionality.
Return Results As has already been explained in Policy Elements, Java lacks the notion of obligations to communicate back to the PEP client additional information about policy decision. This makes it impossible to use JSE-based policies to control data security, for example, as explained in Consistency of Data and Function Security.
Amazon.in Widgets