Sensitive operations must be protected by security manager checks. Refer to guideline ENV02-J. Create a secure sandbox using a Security Manager to learn about the importance of performing security checks and limiting code to a secure sandbox.
Noncompliant Code Example
This noncompliant code example instantiates a Hashtable
and defines a remove()
method to allow the removal of its entries. However, the method is public
and non-final, which leaves it susceptible to malicious callers.
Code Block | ||
---|---|---|
| ||
class SensitiveHash { Hashtable<Integer,String> ht = new Hashtable<Integer,String>(); public void removeEntry(Object key) { ht.remove(key); } } |
Compliant Solution
This compliant solution demonstrates how a security check can be installed to protect entries from being maliciously removed from the Hashtable
instance. A SecurityException
is thrown if the caller does not possess the java.security.SecurityPermission
removeKeyPermission
.
Code Block | ||
---|---|---|
| ||
class SensitiveHash { Hashtable<Integer,String> ht = new Hashtable<Integer,String>(); void removeEntry(Object key) { check("removeKeyPermission"); ht.remove(key); } private void check(String directive) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkSecurityAccess(directive); } } } |
The SecurityManager.checkSecurityAccess()
method determines whether the action controlled by the particular permission is allowed or not.
Noncompliant Code Example
This noncompliant code example uses the SecurityManager.checkRead()
method to check whether the file schema.dtd
can be read from the file system. The problem with the check*()
methods is that fine grained access control is not possible, that is, the result of the check can only be black and white. There is no way to enforce that all files with the dtd
extension are allowed to be read whereas access to others is blocked. New code should rarely use the check*()
APIs because the default implementations of the Java API already use them to protect sensitive operations.
Code Block | ||
---|---|---|
| ||
SecurityManager sm = System.getSecurityManager(); if(sm != null) { //check if file can be read sm.checkRead("/local/schema.dtd"); } |
Compliant Solution
Two methods, checkPermission(Permission perm)
and checkPermission(Permission perm, Object context)
, were added to the SecurityManager
class in J2SE 1.2. The motivations for this change were manifold:
- The
checkPermission()
methods eliminated the requirement of hardcoding names of the checks in the method name. - They used only one copy of the complicated algorithms and code for examining the Java runtime by using a common
checkPermission()
method. - Newer permissions for resources could be easily added by encapsulating them in a new
Permission
subclass.
The single argument checkPermission()
method uses the context of the currently executing thread environment to perform the checks. If the context has the permissions defined in the local policy file, the check succeeds, otherwise a SecurityException
is thrown.
This compliant solution shows the single argument checkPermission()
method and allows files in the local
directory, with the dtd
extension, to be read. DTDPermission
is a custom permission that enforces this level of access (See guideline SEC10-J. Define custom security permissions for fine grained security for details on creating custom permissions). Even if the java.io.FilePermission
is granted to the application with the action "read", DTD
files will be subject to additional access control.
Code Block | ||
---|---|---|
| ||
SecurityManager sm = System.getSecurityManager(); if(sm != null) { //check if file can be read DTDPermission perm = new DTDPermission("/local/", "readDTD"); sm.checkPermission(perm); } |
Sometimes the security check code exists in one context (such as a worker thread) while the check has to be conducted on a different context, such as another thread. The two argument checkPermission()
method is used in this case. It accepts an AccessControlContext
instance as the context
argument. The effective permissions are not computed as the intersection of the permissions of the two contexts and consist of the permissions of the context
argument only.
Both the single and double argument checkPermission()
methods defer to the single argument java.security.AccessController.checkPermission(Permission perm)
method. When invoked directly, this method operates only on the current execution context and as a result, does not supersede the security manager's two argument version.
There is also another (cleaner and preferable) way to handle the security check from a different context. This is accomplished by taking a snapshot of the current execution context using the java.security.AccessController.getContext()
method that returns an AccessControlContext
object. The AccessControlContext
class itself defines a checkPermission()
method that encapsulates a context instead of accepting the current executing context as a parameter. This is shown below.
Code Block | ||
---|---|---|
| ||
// Take the snapshot of the required context, store in acc and pass it to another context AccessControlContext acc = AccessController.getContext(); // Accept acc in another context and invoke checkPermission() on it acc.checkPermission(perm); |
Risk Assessment
Failing to enforce security checks in code that performs sensitive operations can lead to malicious tampering of sensitive data.
Guideline | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
SEC08-J | high | probable | medium | P12 | L1 |
Automated Detection
TODO
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
Wiki Markup |
---|
\[[API 2006|AA. Java References#API 06]\] |
SEC07-J. Declare classes that derive from a sensitive class or implement a sensitive interface final 02. Platform Security (SEC) SEC09-J. Do not base security checks on untrusted sources