★ wanayoo — archive 1999 http://java.sun.com/security/seccodeguide.htmlNouvelle recherche | Portail wanayoo
 


Security Code Guidelines

Last Modified: 2 February, 2000

Overview

A lot of emphasis has been put into the design and implementation of the JavaTM programming language to keep the system as secure as possible. However, a chain only is as strong as its weakest link, and when you add new system code, you are adding a new link to the security chain. This document is intended to help you write code that is free of security holes. It identifies potential pitfalls that you should avoid and shows you how to write your code so that it will not be vulnerable to security attacks.

The code guidelines are grouped into the following three categories:

Privileged Code Guidelines

The JavaTM 2 Platform's access control mechanism protects system resources from unauthorized access by making sure that the calling code has the appropriate permissions for accessing that resource. Generally when a resource access is attempted, all code traversed by the execution thread up to that point must have appropriate permissions in order for the access to be allowed. There are many cases, however, where system code needs to access a system resource in order to be able to perform its functionality, although the code which called that system service does not have the appropriate permissions for accessing the resource.

As an example, assume client code has RuntimePermission to load a certain native library. It calls upon the loadLibrary service, which, in order to accomplish its task, needs read access to the native library file. However, the client code does not have the appropriate FilePermission that allows it access to this file, and if the execution thread would be checked for FilePermission, the operation would fail, when it should have succeeded.

To solve this problem, the API for privileged blocks has been devised, which allows marking a code block as privileged. When a code block is marked as privileged, it can call services based on its permissions even if some of its callers do not have those permissions.

Please refer to http://java.sun.com/j2se/sdk/1.2/docs/guide/security/doprivileged.html for a detailed description of the API for privileged blocks.

Use privileged code sparingly, if at all. Write your code without using privileged blocks; if your code bumps into a security exception, then consider the possible need to use privileged blocks.

When writing privileged code, the following guidelines should be used.

There are certain privileged services that can be performed by system code on behalf of unauthorized clients even if the code is not encapsulated within a privileged block. Please read carefully the Exceptions to the privileged-block mechanism section, as using these services without care can make your code expose security holes.

Keep privileged code as short as possible

Always try to keep privileged code as short as possible. Remember that when a privileged block gets executed within your code, it can access any resource your code has permissions to access, even if its callers do not have these permissions. For example, if your code is an installed standard extension (which by definition resides in the <java-home>/lib/ext directory), then by default (if the default policy file is not modified) it can load libraries, read any file, read system properties, etc. If these operations happen in privileged blocks, then they can be completed on behalf of unprivileged clients. By enabling privileges for as little code as possible it is much easier to audit the code to make sure it is only accessing the minimal amount of protected resources.

Pitfalls and other things to watch out for

An important concern should be public methods (and/or non-public methods that can get invoked by public methods) which wrap privileged blocks that deal with tainted variables. Here tainted means the variables are set by the caller (i.e., passed in as parameters), and thus not under the control of the privileged code.

For example, consider the following method, which might seem like a handy way to access properties:

  public static String getProp(final String name) {
     return (String) AccessController.doPrivileged(new PrivilegedAction() {
          public Object run() {
               // privileged code goes here, for example:
               return System.getProperty(name);
          }
     });
The problem with this method is that since it is public, anyone can call it, and it takes a tainted parameter, namely the name parameter. This would allow any piece of code (say, an applet) access to all system properties, assuming the above method is in a protection domain that allows access to all system properties (i.e., it is on the boot-classpath). If the getProp function was a protected function within a public class, then this would still be problematic (unless the class is in a restricted package. See  Protecting packages ), because anyone could extend the class and be able to call the function.

Always make sure you are not using tainted variables within your privileged code that could compromise the security of such code. If you do need such helper methods, make sure they are private methods and cannot be called from outside your class.

When to wrap code in a privileged block

You must wrap code in a privileged block when the code performs tasks that would normally not be allowed by an applet or untrusted code, but when the tasks need to be done on behalf of this untrusted code. Such tasks include:


Note that you only need to wrap such calls if they could (and need) be invoked by untrusted code, either directly or indirectly. For example, the compiler does not (and should not) need privileged blocks because it is not run by untrusted code such as an applet inside the appletviewer.

Java Code Guidelines

When writing general code, the following guidelines should be used.

Static fields

Reducing scope

As a rule, reduce the scope of methods and fields as much as possible. Check whether package-private members could be made private, whether protected members could be made package-private/private, etc.

Public methods/fields

Refrain from using public variables. Instead, let the interface to your variables be through accessor methods. In this way it is possible to add centralized security checks, if required.

Make sure that any public method that has access to and/or modifies any sensitive internal states includes a security check.

See the following example in which it is possible that untrusted code could set the value for TimeZone.

    private static TimeZone  defaultZone = null;

    public static synchronized void setDefault(TimeZone zone)
    {
        defaultZone = zone;
    }

Protecting packages

It is sometimes desirable to globally protect a package against access by untrusted code. This section describes a few techniques for doing this.

Make objects immutable if possible

Make your object immutable if possible. If that is not possible, make them cloneable and return copies. If you return objects like arrays, Vectors, Hashtables, etc., remember that these objects are not immutable, and the caller can change the contents of these objects which may have security implications. Additionally, immutable objects can improve concurrency, since no locking is needed. See Clear sensitive information for an exception to this rule.

Never return a reference to an internal array that contains sensitive data

This is just a variant on the immutable rule, but is mentioned here because it is a common mistake. Even if your array contains objects that are immutable (such as Strings), you need to return a copy so the the caller cannot change which Strings are in the array. Instead of passing back an array, make a copy of the array and return the copy.

Never store user given array of objects directly

This is another variant of the immutable rule. Constructors and methods taking arrays of objects, such as arrays of PublicKeys, should clone the arrays before saving them internally rather then directly assigning the array reference to an internal variable of similar type. Without this precaution, any changes made by the user to the external array (after creating the object using the constructor in question) could accidentally change the internal state of the object even though the object might otherwise be immutable.

Serialization

When an object is serialized - and until it is deserialized - it is outside of the control of the Java runtime environment, and is therefore is outside the control of the security provided by the Java platform.

Here are some things to keep in mind when implementing the Serializable interface:

Native methods

Native methods should be examined for:

Clear Sensitive Information

When storing sensitive information such as credentials, strive to keep it in mutable data types such as arrays rather than in immutable objects such as Strings. This will allow the sensitive information to be explicitly cleared at the earliest possible time. Do not trust the Java platform's automatic garbage collection to do that for you because the memory may never be reclaimed by the collector, or it may be reclaimed much later on. Clearing this information as soon as possible makes a heap-inspection attack from outside the virtual machine more difficult.


C Code Guidelines

These guidelines aren't rigorous or deep, but they represent a simple checklist for C programs.

Check all input arguments for validity

Here, validity means not just the C type, but also program-specific type information. For example, if an input argument is supposed to be an executable file, check that the input file is an executable and that the user has permission to run the file.

Never use the unix "system()" call

The system() command is a very flexible command, but it will try to execute whatever you pass to it. Don't use the system command inside utilities. For the same reason, do not invoke shell(), popen or exec*p from inside your code. Additionally, don't use setuid on a whole program. Just use setuid root for the small part where you do need to be root, and then setuid back to the user.

Never use scanf; use fgetc; use Java software alternatives for printf

Scanf will read anything. It will do all the tedious formatting for you, which makes it attractive to use. However, its behavior when given a string that doesn't match the format expected is undefined, and often opens up holes. Therefore, don't use scanf to read input from users. Use fgetc.

There are Java software alternatives to printf that should be used to avoid stack overflows when hackers try to have some internal function print out a string that is too long.

    jio_fprintf
    jio_snprintf
    jio_vsnprintf
In 1.1.x the jio functions are found in src/share/sun/jio.c, and in the build, build/lib/sparc/green_threads/libjava.so

In 1.2 the jio functions are found in src/share/javavm/runtime/util.c, and in the build, build/solaris/lib/sparc/libjava.so.

Check environment variables for validity

Don't just read them and use them.

Beware of setuid root, and beware of programs that ship with setuid root.

Only run with setuid as root for the very short time that you need to actually DO something as root. Try to not setuid root at all. Create a new user, like "foo" or "duke", and use that UID. The OS reserves the first 200 UIDs for internal use, so there are plenty of UIDs that can be defined. This reservation of UIDs is part of the ABI. If you MUST change uid, use seteuid first rather than setuid, and use setreuid as a backup. This makes it easy to revert back to either the real uid or the saved uid.
 

Avoid  setuid shell-scripts altogether!!!

setuid shell-scripts are even more dangerous than setuid programs, as they can be subverted in many ways.
Consequently use of setuid shell-scripts is not recommended.

Never open a file as root

Don't open the file as root. Open the file as user. If you need to be root, first open the file as user, then become root, then go back to user before changing the file.

Check all functions for valid returns

If a function doesn't return a valid value, notice it and then do the right thing. Don't ignore return values.

Strip binaries

Believe it or not, a lot of information can be obtained from the binaries just by running "strings" on them.

Consider logging things like UIDs, file accesses, and so on.

Don't use syslog for logging purposes, though. Syslog does not check the file system environment variable for validity.
 

Don't use chmod(), chown(), chgrp(): Use fchmod(), fchown() instead

The rational behind this recommendation is the following. Compare the following two code fragments:
 

code snippet #1
"
fd = open("name");
write(fd, "#!/bin/sh...", 24);
close(fd);
chmod("name");
"

and...

code snippet #2
"
fd = open("name");
write(fd, "#!/bin/sh...", 24);
fchmod(fd, 666);
close(fd);
"

If someone races you, they can do the following

if (access("name") == OK)
        {
        remove("name");
        symlink("name", "target_name");
        }

Now in code snippet #1, you may have just chmod'd your target_name, which could be something like /etc/passwd.

Apart from this security consideration, code snippet #2 is more efficient because fchmod(...) does not have to do a separate look-up of the name, whereas chmod(..) must do so.


Copyright © 2000 Sun Microsystems, Inc. All Rights Reserved.

Please send comments to: java-security@sun.com
Sun