Java Security Basic

 

Overview

Language such as C++, C could give you a lot trouble in security.

It is all or nothing, once you get a hold of a pointer. You can do arithmetic, walk with it freely in memory. Well, do anything.

Java as a "secure" language. Has a lot of build-in Language-Level Security

 

Garbage Collection

No manual hard code of delete or free of memory

When no reference is attached to a piece of memory, the Java garbage collector release the memory

Freed memory rejoin the free storage pool, and can be used by another program

In JRE 1.2, there is no asynchronous running of garbage collector

There is also no turning off of the collector

Stack memory allocation is also in Java.

Multiple per program with each executing thread per stack

Only local variables of a method are allocated in the stack

 

Controlled Memory Access

No pointer arithmetic

Clearly defines behavior to uninitialized variables

All heap-based memory is automatically initialized

All stack-based memory is not

Thus, all class and instance variables are never set to undefined values

All local variables must definitely be assigned before use or the source compiler is obligated to give you an error

 

Build-in Language Features

Strong (than C++) compile-time type-checking

Cannot cast object back and for like C++

final modifier which disallows subclassing and overriding

If final is applied to object, then it cannot later point to another object (no changing)

There is a concept called blank finals. Make all the data member in a class final. Then build the constructor to initize the data member (for every constructor). Thus, the member field cannot be accidentially altered after the object has been constructed.

 

JVM Level Security

JVM is the secure sandbox

JVM verifies all classes in the sandbox

It is a fense to prevent unwanted access to the outside (e.g. the OS JVM runs on)

The best you can do is denial-of-service that take 100% of CPU

 

Java Byte Codes and Verification

The machine language for JVM

Cross compilers exist besides Java. For example, Ada, COBOL, etc.

When class is loaded, the first thing is JVM inspecting validity of Java byte codes

Four pass process in verification

1. Checking magic number, the first four bytes of the file are 0x CAFEBABE

2. Check superclass is not Object

3. Code verification to ensure opcodes are still valid. Make sure method arguments are proper type and opcode arguments are right

4. Performed when a method is invoked. Verifiy member access with public protected, default, or private

In JDK 1.2, only class loaded over the network is checked

"Can't find class" error would be found

 

Class Loading Concept in Security

ClassLoader finds and loads the class

ClassLoader will not load any class in java.* package (the core libraries classes). No false representation of the core

Separate name space for classes from different locations (could have the same class name)

Different location classes would not communicate. Untrusted programs cannot get info from trusted programs

loadClass concept

// within loadClass()
Class c = findLoadedClass (name);

if (c == null) 
{
  try 
  { 
    // If cannot load the class, find it
    c = findSystemClass (name);
  } 
  catch (Exception e) 
  {
    // Ignore these exceptions.
  }
}

if (c == null) 
{
  // User defined loadClassData, get it from database, network, etc
  byte data[] = loadClassData(name);

  // Covert byte into a class
  c = defineClass (name, data, 0, data.length);
  
  if (c == null)
    throw new ClassNotFoundException (name);

  if (resolve)
     resolveClass (c);

}

return c;

 

Build your own class loader

FileClassLoader.java

import java.io.*;
import java.net.*;
import java.util.*;

//
// Example for loading a class from a file
//
public class FileClassLoader extends ClassLoader
{
  private String root;

  public FileClassLoader (String rootDir)
  {
    if (rootDir == null)
      throw new IllegalArgumentException ("No file directory provided");
    root = rootDir;
  }

  protected Class loadClass (String name, boolean resolve) throws ClassNotFoundException
  {
    // Since all support classes of loaded class use same class loader
    // must check subclass cache of classes for things like Object
    Class c = findLoadedClass (name);

    if (c == null)
    {
      try
      {
        c = findSystemClass (name);
      }
      catch (Exception e)
      {
        // Do nothing if system class is not the one
      }
    }

    if (c == null)
    {
      // Convert class name argument to filename
      // Convert package names into subdirectories
      String filename = name.replace ('.', File.separatorChar) + ".class";

      try
      {
        byte data[] = loadClassData(filename);
        c = defineClass (name, data, 0, data.length);
        if (c == null)
          throw new ClassNotFoundException (name);
      }
      catch (IOException e)
      {
        throw new ClassNotFoundException ("Error reading file: " + filename);
      }
    }

    if (resolve)
      resolveClass (c);
    return c;
  }

  private byte[] loadClassData (String filename) throws IOException
  {
    // Create a file object relative to directory provided
    File f = new File (root, filename);

    System.out.println("INFO: rootdir is [" + root + "]");

    // Get size of class file
    int size = (int)f.length();

    // Reserve space to read
    byte buff[] = new byte[size];

    // Get stream to read from
    FileInputStream fis = new FileInputStream(f);
    DataInputStream dis = new DataInputStream (fis);

    // Read in data
    dis.readFully (buff);

    // close stream
    dis.close();

    // return data
    return buff;
  }
}

CLTester.java

public class CLTester
{
  public static void main (String args[]) throws Exception
  {
    FileClassLoader loader = new FileClassLoader(args[0]);
    // Load the class with my own file
    Class c = loader.loadClass (args[1]);
    Object tester = c.newInstance();
  }
}

Running the Class Loader Tester

java CLTester C:\sinn\Java\security\classloader\testdir Tester

 

URLClassLoader

URLTester.java

import java.net.*;
import java.io.*;



// Indicate the location of the class to load
public class URLTester
{
  public static void main (String args[]) throws Exception
  {
    // Create an array of URLs
    // From directory name specified as args[0]
    URL urlList[] = {new File(args[0]).toURL()};

    // Create a URLClassLoader
    ClassLoader loader = new URLClassLoader(urlList);

    // Load class from class loader
    // Use args[1] as class name
    Class c = loader.loadClass (args[1]);

    // Create an instance of the class just loaded
    Object tester = c.newInstance();
  }
}

Run

java URLTester C:\sinn\Java\security\classloader\testdir Tester

 

Runtime Checking

Most type-checking can be done at compile time

Sometimes type of an object can only be found out in runtime

JVM ensure that for example, subclass cannot access data specifically defined for superclass

JVM also ensures you do not try to pass off something that is not part of the class hierarchy as one of its own

ArrayIndexOutOfBoundsException is thown when walking pass from an array

 

Security Management

Security Manager

Each instance of JVM has a SecurityManager

Once a manager is installed, it cannot be replaced

Replacement will cause SecurityException to be thrown

SecurityManager provides a "hard coded" security policy

E.g. browser's security manager prevents applets loaded over the network read or write files in local file system

For every operations, the Java program checks with the SecurityManager to see if the operation is allowed or not

SecurityException thrown if fails. It is a RuntimeException, so try catch block is not needed

 

Write your own SecurityManager

SMTester.java

import java.io.*;

public class SMTester
{
  public static void main (String args[])
  {
    try
    {
      File f = new File("joe.txt");
      System.out.println ("joe.txt exists? " + f.exists());
    }
    catch (SecurityException e)
    {
      System.err.println ("Cannot check if joe.txt exists");
    }

    System.setSecurityManager(new MySecurityManager());
    System.out.println ("\nAfter new SM Installed\n");

    // After the new security manager is loaded
    // only .tmp extension can be checked. (To show as a demo)

    try
    {
      File f = new File("joe.txt");
      System.out.println ("joe.txt exists? " + f.exists());
    }
    catch (SecurityException e)
    {
      System.err.println ("Cannot check if joe.txt exists");
    }

    try
    {
      File f = new File("joe.tmp");
      System.out.println ("joe.tmp exists? " + f.exists());
    }
    catch (SecurityException e)
    {
      System.err.println ("Cannot check if joe.tmp exists");
    }

    try
    {
      File f = new File("joe.txt");
      System.out.println ("joe.txt writable? " + f.canWrite());
    }
    catch (SecurityException e)
    {
      System.err.println ("Cannot check if joe.txt writable");
    }

    try
    {
      File f = new File("joe.tmp");
      System.out.println ("joe.tmp writable? " + f.canWrite());
    }
    catch (SecurityException e)
    {
      System.err.println ("Cannot check if joe.tmp writable");
    }

  }
}

NullSecurityManager.java

import java.io.*;
import java.net.*;

class NullSecurityManager extends SecurityManager
{
  public void checkCreateClassLoader() { }
  public void checkAccess(Thread g) { }
  public void checkAccess(ThreadGroup g) { }
  public void checkExit(int status) { }
  public void checkExec(String cmd) { }
  public void checkLink(String lib) { }
  public void checkRead(FileDescriptor fd) { }
  public void checkRead(String file) { }
  public void checkRead(String file, Object context) { }
  public void checkWrite(FileDescriptor fd) { }
  public void checkWrite(String file) { }
  public void checkDelete(String file) { }
  public void checkConnect(String host, int port) { }
  public void checkConnect(String host, int port, Object context) { }
  public void checkListen(int port) { }
  public void checkAccept(String host, int port) { }
  public void checkMulticast(InetAddress maddr) { }
  public void checkMulticast(InetAddress maddr, byte ttl) { }
  public void checkPropertiesAccess() { }
  public void checkPropertyAccess(String key) { }
  public void checkPropertyAccess(String key, String def) { }
  public boolean checkTopLevelWindow(Object window) { return true; }
  public void checkPrintJobAccess() { }
  public void checkSystemClipboardAccess() { }
  public void checkAwtEventQueueAccess() { }
  public void checkPackageAccess(String pkg) { }
  public void checkPackageDefinition(String pkg) { }
  public void checkSetFactory() { }
  public void checkMemberAccess(Class clazz, int which) { }
  public void checkSecurityAccess(String provider) { }
}

MySecurityManager.java

import java.io.*;

public class MySecurityManager extends NullSecurityManager
{

  // Only check file with .tmp extension

  final static String filenameEnding = ".tmp";

  public void checkRead(String file)
  {
    if (!file.endsWith(filenameEnding))
    {
      throw new SecurityException ("My exception: Read attempt: " + file);
    }
  }

  public void checkRead(String file, Object context)
  {
    checkRead (file);
  }

  public void checkWrite(String file)
  {
    if (!file.endsWith(filenameEnding))
    {
      throw new SecurityException ("My exception: Write attempt: " + file);
    }
  }

}

Run

> java SMTester


joe.txt exists? false

After new SM Installed

Cannot check if joe.txt exists
joe.tmp exists? false
Cannot check if joe.txt writable
joe.tmp writable? false