defensive-coding-guide/en-US/Java-SecurityManager.xml
2017-10-23 15:25:31 +00:00

292 lines
12 KiB
XML

<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<section id="sect-Defensive_Coding-Java-SecurityManager">
<title>Interacting with the Security Manager</title>
<para>
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 <emphasis>security
manager</emphasis> draws a line between fully trusted, partially
trusted and untrusted code.
</para>
<para>
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
<literal>java.io.FileOutputStream</literal>.) Instead, critical
functionality is protected by <emphasis>stack
inspection</emphasis>: 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.
</para>
<para>
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 (<xref
linkend="sect-Defensive_Coding-Java-SecurityManager-Privileged"/>).
</para>
<para>
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).
</para>
<section id="sect-Defensive_Coding-Java-SecurityManager-Compatible">
<title>Security Manager Compatibility</title>
<para>
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.
</para>
<itemizedlist>
<listitem>
<para>
When retrieving system properties using
<function>System.getProperty(String)</function> or similar
methods, catch <literal>SecurityException</literal>
exceptions and treat the property as unset.
</para>
</listitem>
<listitem>
<para>
Avoid unnecessary file system or network access.
</para>
</listitem>
<listitem>
<para>
Avoid explicit class loading. Access to a suitable class
loader might not be available when executing as untrusted
code.
</para>
</listitem>
</itemizedlist>
<para>
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 <xref
linkend="sect-Defensive_Coding-Java-SecurityManager-Privileged"/>.
</para>
</section>
<section id="sect-Defensive_Coding-Java-SecurityManager-Activate">
<title>Activating the Security Manager</title>
<para>
The usual command to launch a Java application,
<command>java</command>, 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 <xref
linkend="sect-Defensive_Coding-Java-SecurityManager-Unprivileged"/>).
</para>
<para>
The <option>-Djava.security.manager</option> 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 <xref
linkend="ex-Defensive_Coding-Java-SecurityManager-GrantAll"/>
has been saved in a file <filename>grant-all.policy</filename>,
this policy can be activated using the option
<option>-Djava.security.policy=grant-all.policy</option> (in
addition to the <option>-Djava.security.manager</option>
option).
</para>
<example id="ex-Defensive_Coding-Java-SecurityManager-GrantAll">
<title>Most permissve OpenJDK policy file</title>
<programlisting>
grant {
permission java.security.AllPermission;
};
</programlisting>
</example>
<para>
With this most permissive policy, the security manager is still
active, and explicit requests to drop privileges will be
honored.
</para>
</section>
<section id="sect-Defensive_Coding-Java-SecurityManager-Unprivileged">
<title>Reducing Trust in Code</title>
<para>
<xref linkend="ex-Defensive_Coding-Java-SecurityManager-Unprivileged"/>
shows how to run a piece code of with reduced privileges.
</para>
<example id="ex-Defensive_Coding-Java-SecurityManager-Unprivileged">
<title>Using the security manager to run code with reduced
privileges</title>
<xi:include href="snippets/Java-SecurityManager-Unprivileged.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
The example above does not add any additional permissions to the
<literal>permissions</literal> object. If such permissions are
necessary, code like the following (which grants read permission
on all files in the current directory) can be used:
</para>
<informalexample>
<xi:include href="snippets/Java-SecurityManager-CurrentDirectory.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</informalexample>
<important>
<para>
Calls to the
<function>java.security.AccessController.doPrivileged()</function>
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
<function>doPrivileged()</function> has returned, even to
objects created by the code which ran with reduced privileges.
(This applies to object finalization in particular.)
</para>
<para>
The example code above does not prevent the called code from
calling the
<function>java.security.AccessController.doPrivileged()</function>
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.
</para>
<para>
The <literal>context</literal> argument in <xref
linkend="ex-Defensive_Coding-Java-SecurityManager-Unprivileged"/>
is extremely important—otherwise, this code would increase
privileges instead of reducing them.
</para>
</important>
<para>
For activating the security manager, see <xref
linkend="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.
</para>
</section>
<section id="sect-Defensive_Coding-Java-SecurityManager-Privileged">
<title>Re-gaining Privileges</title>
<para>
Ordinarily, when trusted code is called from untrusted code, it
loses its privileges (because of the untrusted stack frames
visible to stack inspection). The
<function>java.security.AccessController.doPrivileged()</function>
family of methods provides a controlled backdoor from untrusted
to trusted code.
</para>
<important>
<para>
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.
</para>
</important>
<para>
In essence, the <function>doPrivileged()</function> methods
cause the stack inspection to end at their call site. Untrusted
code further down the call stack becomes invisible to security
checks.
</para>
<para>
The following operations are common and safe to perform with
elevated privileges.
</para>
<itemizedlist>
<listitem>
<para>
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.)
</para>
</listitem>
<listitem>
<para>
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.
</para>
</listitem>
<listitem>
<para>
Accessing network resources under a fixed address, name or
URL, derived from a system property or configuration file,
information leaks not withstanding.
</para>
</listitem>
</itemizedlist>
<para>
<xref linkend="ex-Defensive_Coding-Java-SecurityManager-Privileged"/>
shows how to request additional privileges.
</para>
<example id="ex-Defensive_Coding-Java-SecurityManager-Privileged">
<title>Using the security manager to run code with increased
privileges</title>
<xi:include href="snippets/Java-SecurityManager-Privileged.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
Obviously, this only works if the class containing the call to
<function>doPrivileged()</function> is marked trusted (usually
because it is loaded from a trusted class loader).
</para>
<para>
When writing code that runs with elevated privileges, make sure
that you follow the rules below.
</para>
<itemizedlist>
<listitem>
<para>
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.
</para>
</listitem>
<listitem>
<para>
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.
</para>
</listitem>
<listitem>
<para>
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.
</para>
</listitem>
</itemizedlist>
<para>
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 <xref
linkend="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.)
</para>
<example id="ex-Defensive_Coding-Java-SecurityManager-Callback">
<title>Restoring privileges when invoking callbacks</title>
<xi:include href="snippets/Java-SecurityManager-Callback.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
</section>
</section>