Upcoming Events

Cloud Connect
Santa Clara
Feb 13-16, 2012

Cloud Connect brings together the entire cloud eco-system to better understand the transformation we're experiencing and promises to be the defining event of the cloud computing industry. Learn about the latest cloud technologies and platforms from thought leaders in Cloud Connect’s comprehensive conference.

Register Now!

More Events »

Subscribe to Newsletter

  • Keep up with all of the latest news and analysis on the fast-moving IT industry with Network Computing newsletters.
Sign Up



  W O R K S H O P
Writing Secure Java Programs

May 1, 2000
By Elias Levy



Java offers some unique advantages and several drawbacks when it comes to writing secure programs. Because of Java's cross-platform nature, and the fact that its API is based on the minimum common denominator of the features supported by the underlying platforms, Java programs can't employ security techniques common to other programming languages. Java, however, does enforce a number of security measures. This article describes the status of Java from a programmer's point of view with regard to a variety of security features and attacks.

Buffer Overflows

The widespread use of the C language has made array-bounds-checking errors one of the most ubiquitous software faults, consequently making buffer-overflow attacks very common. These attacks also proliferate because such faults can be exploited remotely via network inputs; a variety of them can be used to modify function activation records, letting an attacker change the flow of the program and potentially execute arbitrary code.

This is one security area in which Java measures up. The Java language frees the programmer from having to perform array-bounds checking. The language performs all array-bounds checking. Furthermore, the language provides a string data type that is easy to use and manipulate. The programmer no longer has to user character arrays as strings, no longer has to make sure they are properly terminated, and no longer has to make sure the character array allocated is big enough to hold a string.

As we will see later, Java's array-bounds checking is not the result of its designers trying to create a language/environment that makes it easy to write secure programs, but rather the result of trying to create a language/environment that can execute malicious programs securely. By "executing malicious code securely," we mean that you can execute a program that attempts but does not succeed in performing illegal tasks. Java excels at executing malicious programs securely, which should not be confused with writing secure programs.

File Access

Java can perform only very basic file security operations. It can test whether the application running in its current security context can read or write to a file. From within Java you cannot access or change file permissions or ACLs (access control lists). The file creation functions do not let you set the initial file's permissions, leaving the default permission unspecified and implementation-dependent.

The semantics of java.io.File.createNewFile() guarantee that file creation is "atomic" with respect to all other file-system operations, which means no other operation will occur before it finishes. A file will not be opened if it already exists. Yet there is no mention in the API documentation as to whether the function will follow file-system indirections (dangling symbolic links in Unix) while creating the file. File-system indirections can be used to fool privileged programs to manipulate files other than those they intended.

java.io.File.createTempFile() can be used to create temporary files. No explicit guarantee is given as to whether the file creation is atomic, but since it's likely the function uses java.io.createNewFile() to create the file, we can extend the guarantee of that function to this one. No guarantees are made as to the randomness of the file name nor as to the search space of the file name (number of possible temporary file names). The only comment indicates that the file names will include five or more internally generated characters. An easily guessable file name may let a malious user trick the Java program into manipulating the wrong file. The default location of the temporary files created is determined by the java.io.tmpdir system property. On Unix systems the default value of this property is "/tmp" or "/var/tmp"; on Win32 systems it is "C:\tmp". In both cases these are publicly readable and writable directories. The java.io.File.createTempFile() method lets you pass the function a directory, where it creates the file. It is recommended you pass it a private directory that can be used to store the temporary files.

The constructor of the classes java.io.RandomAccessFile, java.io.InputStream, java.io.OutputStream, java.io.FileReader and java.io.FileWriter can also be used to open and/or create files, but there is no guarantee that if you attempt to create a file, you will not open an existing file of the same name or that it will not follow file-system indirections.

Notice that none of these file-creation APIs lets you state what the file's initial permissions or ACL should be. This leaves programs written in Java vulnerable to local attacks if default file permissions are lax.

Input Validation

Input-validation errors are a common source of vulnerabilities, especially in applications that invoke other programs or accept input that will be used as a file name. Java has a mixed score in this area. The classes java.io.StreamTokenizer and java.io.DataInputStream make it easy to parse some types of input but are not suitable as generic parsing or filtering functions. The base Java API is sorely lacking a regular expression class. As an alternative you can use the gnu.regexp package[8]. While no class will take care of input validation problems, a regular expression class can make it easier to handle the process correctly.

Numerical Under/Overflows and Conversions

Java programmers must also keep an eye out for underflow and overflow of numerical types. Java will not produce an exception when they occur. They can lead to security vulnerabilities when assumptions are made about the sign or magnitude of variables. You also must take note of conversions between the primitive numerical types; they can be a result of lost information and positive numbers becoming negative, or vice versa. The upside is that the Java compiler will fail to compile a source file if it finds a narrowing primitive conversion that is not an explicit cast. So be careful when doing explicit casts between primitive numerical types.

Resource Attacks

Java does not support resource limits. A Java program could be fooled into allocating large amounts of resources. An ugly hack to keep track of the available run-time environment memory is to use the java.lang.Runtime.freeMemory() method, backing off if memory becomes scarce.

Origin Validation

Secure programs are required to strongly validate the origin of requests for privileged services. Java is weak in the area of authenticating local or remote users, as it does not have an API to query the local system for the identity of its own process, nor does it support any API and protocols to authenticate remote users. The best you can do is query the IP remote address and port number of a network connection via the java.net.Socket.getInetAddress() and java.net.Socket.getPort() methods. Java should support a generic security authentication API such as GSS-API or SSL. The new JAAS API may solve some of these issues.

Finally, as a Java programmer you can use the features described in "Executing Java Programs Securely" to create a security policy that your application must follow, and to tell the Java run time to enforce them. Writing secure programs and executing programs securely are two faces of the same coin.



PAGE: 1 I 2 I 3 I NEXT PAGE
 

Research and Reports

Hypervisor Derby
August 2011

Network Computing: August 2011

TechWeb Careers