256 lines
8.9 KiB
Text
256 lines
8.9 KiB
Text
|
|
:experimental:
|
|
|
|
[[sect-Defensive_Coding-Java-SecurityManager]]
|
|
==== Interacting with the Security Manager
|
|
|
|
The Java platform is largely implemented in the Java language
|
|
itself. Therefore, within the same JVM, code runs which is part
|
|
of the Java installation and which is trusted, but there might
|
|
also be code which comes from untrusted sources and is restricted
|
|
by the Java sandbox (to varying degrees). The *security
|
|
manager* draws a line between fully trusted, partially
|
|
trusted and untrusted code.
|
|
|
|
The type safety and accessibility checks provided by the Java
|
|
language and JVM would be sufficient to implement a sandbox.
|
|
However, only some Java APIs employ such a capabilities-based
|
|
approach. (The Java SE library contains many public classes with
|
|
public constructors which can break any security policy, such as
|
|
`java.io.FileOutputStream`.) Instead, critical
|
|
functionality is protected by *stack
|
|
inspection*: At a security check, the stack is walked
|
|
from top (most-nested) to bottom. The security check fails if a
|
|
stack frame for a method is encountered whose class lacks the
|
|
permission which the security check requires.
|
|
|
|
This simple approach would not allow untrusted code (which lacks
|
|
certain permissions) to call into trusted code while the latter
|
|
retains trust. Such trust transitions are desirable because they
|
|
enable Java as an implementation language for most parts of the
|
|
Java platform, including security-relevant code. Therefore, there
|
|
is a mechanism to mark certain stack frames as trusted (<<sect-Defensive_Coding-Java-SecurityManager-Privileged>>).
|
|
|
|
In theory, it is possible to run a Java virtual machine with a
|
|
security manager that acts very differently from this approach,
|
|
but a lot of code expects behavior very close to the platform
|
|
default (including many classes which are part of the OpenJDK
|
|
implementation).
|
|
|
|
[[sect-Defensive_Coding-Java-SecurityManager-Compatible]]
|
|
===== Security Manager Compatibility
|
|
|
|
A lot of code can run without any additional permissions at all,
|
|
with little changes. The following guidelines should help to
|
|
increase compatibility with a restrictive security manager.
|
|
|
|
* When retrieving system properties using
|
|
`System.getProperty(String)` or similar
|
|
methods, catch `SecurityException`
|
|
exceptions and treat the property as unset.
|
|
|
|
* Avoid unnecessary file system or network access.
|
|
|
|
* Avoid explicit class loading. Access to a suitable class
|
|
loader might not be available when executing as untrusted
|
|
code.
|
|
|
|
If the functionality you are implementing absolutely requires
|
|
privileged access and this functionality has to be used from
|
|
untrusted code (hopefully in a restricted and secure manner),
|
|
see <<sect-Defensive_Coding-Java-SecurityManager-Privileged>>.
|
|
|
|
[[sect-Defensive_Coding-Java-SecurityManager-Activate]]
|
|
===== Activating the Security Manager
|
|
|
|
The usual command to launch a Java application,
|
|
[command]`java`, does not activate the security manager.
|
|
Therefore, the virtual machine does not enforce any sandboxing
|
|
restrictions, even if explicitly requested by the code (for
|
|
example, as described in <<sect-Defensive_Coding-Java-SecurityManager-Unprivileged>>).
|
|
|
|
The [option]`-Djava.security.manager` option activates
|
|
the security manager, with the fairly restrictive default
|
|
policy. With a very permissive policy, most Java code will run
|
|
unchanged. Assuming the policy in <<ex-Defensive_Coding-Java-SecurityManager-GrantAll>>
|
|
has been saved in a file `grant-all.policy`,
|
|
this policy can be activated using the option
|
|
[option]`-Djava.security.policy=grant-all.policy` (in
|
|
addition to the [option]`-Djava.security.manager`
|
|
option).
|
|
|
|
[[ex-Defensive_Coding-Java-SecurityManager-GrantAll]]
|
|
.Most permissve OpenJDK policy file
|
|
====
|
|
|
|
[subs="quotes"]
|
|
----
|
|
|
|
grant {
|
|
permission java.security.AllPermission;
|
|
};
|
|
|
|
----
|
|
|
|
====
|
|
|
|
With this most permissive policy, the security manager is still
|
|
active, and explicit requests to drop privileges will be
|
|
honored.
|
|
|
|
[[sect-Defensive_Coding-Java-SecurityManager-Unprivileged]]
|
|
===== Reducing Trust in Code
|
|
|
|
The <<ex-Defensive_Coding-Java-SecurityManager-Unprivileged>> example
|
|
shows how to run a piece code of with reduced privileges.
|
|
|
|
[[ex-Defensive_Coding-Java-SecurityManager-Unprivileged]]
|
|
.Using the security manager to run code with reduced privileges
|
|
====
|
|
|
|
[subs="quotes"]
|
|
----
|
|
include::snippets/Java-SecurityManager-Unprivileged.adoc[]
|
|
|
|
----
|
|
|
|
====
|
|
|
|
The example above does not add any additional permissions to the
|
|
`permissions` object. If such permissions are
|
|
necessary, code like the following (which grants read permission
|
|
on all files in the current directory) can be used:
|
|
|
|
[subs="quotes"]
|
|
----
|
|
include::snippets/Java-SecurityManager-CurrentDirectory.adoc[]
|
|
|
|
----
|
|
|
|
[IMPORTANT]
|
|
====
|
|
|
|
Calls to the
|
|
`java.security.AccessController.doPrivileged()`
|
|
methods do not enforce any additional restriction if no
|
|
security manager has been set. Except for a few special
|
|
exceptions, the restrictions no longer apply if the
|
|
`doPrivileged()` has returned, even to
|
|
objects created by the code which ran with reduced privileges.
|
|
(This applies to object finalization in particular.)
|
|
|
|
The example code above does not prevent the called code from
|
|
calling the
|
|
`java.security.AccessController.doPrivileged()`
|
|
methods. This mechanism should be considered an additional
|
|
safety net, but it still can be used to prevent unexpected
|
|
behavior of trusted code. As long as the executed code is not
|
|
dynamic and came with the original application or library, the
|
|
sandbox is fairly effective.
|
|
|
|
The `context` argument in <<ex-Defensive_Coding-Java-SecurityManager-Unprivileged>>
|
|
is extremely important—otherwise, this code would increase
|
|
privileges instead of reducing them.
|
|
|
|
====
|
|
|
|
For activating the security manager, see <<sect-Defensive_Coding-Java-SecurityManager-Activate>>.
|
|
Unfortunately, this affects the virtual machine as a whole, so
|
|
it is not possible to do this from a library.
|
|
|
|
[[sect-Defensive_Coding-Java-SecurityManager-Privileged]]
|
|
===== Re-gaining Privileges
|
|
|
|
Ordinarily, when trusted code is called from untrusted code, it
|
|
loses its privileges (because of the untrusted stack frames
|
|
visible to stack inspection). The
|
|
`java.security.AccessController.doPrivileged()`
|
|
family of methods provides a controlled backdoor from untrusted
|
|
to trusted code.
|
|
|
|
[IMPORTANT]
|
|
====
|
|
|
|
By design, this feature can undermine the Java security model
|
|
and the sandbox. It has to be used very carefully. Most
|
|
sandbox vulnerabilities can be traced back to its misuse.
|
|
|
|
====
|
|
|
|
In essence, the `doPrivileged()` methods
|
|
cause the stack inspection to end at their call site. Untrusted
|
|
code further down the call stack becomes invisible to security
|
|
checks.
|
|
|
|
The following operations are common and safe to perform with
|
|
elevated privileges.
|
|
|
|
* Reading custom system properties with fixed names,
|
|
especially if the value is not propagated to untrusted code.
|
|
(File system paths including installation paths, host names
|
|
and user names are sometimes considered private information
|
|
and need to be protected.)
|
|
|
|
* Reading from the file system at fixed paths, either
|
|
determined at compile time or by a system property. Again,
|
|
leaking the file contents to the caller can be problematic.
|
|
|
|
* Accessing network resources under a fixed address, name or
|
|
URL, derived from a system property or configuration file,
|
|
information leaks not withstanding.
|
|
|
|
The <<ex-Defensive_Coding-Java-SecurityManager-Privileged>> example
|
|
shows how to request additional privileges.
|
|
|
|
[[ex-Defensive_Coding-Java-SecurityManager-Privileged]]
|
|
.Using the security manager to run code with increased privileges
|
|
====
|
|
|
|
[subs="quotes"]
|
|
----
|
|
include::snippets/Java-SecurityManager-Privileged.adoc[]
|
|
|
|
----
|
|
|
|
====
|
|
|
|
Obviously, this only works if the class containing the call to
|
|
`doPrivileged()` is marked trusted (usually
|
|
because it is loaded from a trusted class loader).
|
|
|
|
When writing code that runs with elevated privileges, make sure
|
|
that you follow the rules below.
|
|
|
|
* Make the privileged code as small as possible. Perform as
|
|
many computations as possible before and after the
|
|
privileged code section, even if it means that you have to
|
|
define a new class to pass the data around.
|
|
|
|
* Make sure that you either control the inputs to the
|
|
privileged code, or that the inputs are harmless and cannot
|
|
affect security properties of the privileged code.
|
|
|
|
* Data that is returned from or written by the privileged code
|
|
must either be restricted (that is, it cannot be accessed by
|
|
untrusted code), or must be harmless. Otherwise, privacy
|
|
leaks or information disclosures which affect security
|
|
properties can be the result.
|
|
|
|
If the code calls back into untrusted code at a later stage (or
|
|
performs other actions under control from the untrusted caller),
|
|
you must obtain the original security context and restore it
|
|
before performing the callback, as in <<ex-Defensive_Coding-Java-SecurityManager-Callback>>.
|
|
(In this example, it would be much better to move the callback
|
|
invocation out of the privileged code section, of course.)
|
|
|
|
[[ex-Defensive_Coding-Java-SecurityManager-Callback]]
|
|
.Restoring privileges when invoking callbacks
|
|
====
|
|
|
|
[subs="quotes"]
|
|
----
|
|
include::snippets/Java-SecurityManager-Callback.adoc[]
|
|
|
|
----
|
|
|
|
====
|