Sponsored Link •
|
Advertisement
|
As mentioned previously, one of the greatest advantages of Java's sandbox security model is that the sandbox can be customized. The code signing and authentication technology introduced in Java version 1.1 enables your running application to differentiate code to which you attribute different degrees of trust. By customizing the sandbox, trusted code can be given more access to system resources than untrusted code. This prevents untrusted code from accessing the system, but allows trusted code to access the system and do useful work. The real power of Java's security architecture, however, lies in the ability to grant code with varying degrees of trust different levels of partial access to the system.
Microsoft offers an authentication technology similar to Java's for ActiveX controls, but ActiveX controls don't run inside a sandbox. Thus with ActiveX, a chunk of mobile code is either completely trusted or completely untrusted. If untrusted, the ActiveX control is denied the opportunity to run. If trusted, the ActiveX control is allowed to run and given full access to the system. While this is a big improvement over no authentication at all, if some malicious or buggy code gets authenticated, the dangerous code has full access to the system. One of the strengths of Java's security architecture is that code can be given access only to the resources it needs. If some malicious or buggy code gets authenticated, it has less opportunity to do damage. For example, instead of being able to delete all files on a local hard disk, the malicious or buggy code might only be able to delete the files in a particular directory set aside just for it.
One major goal of the 1.2 security infrastructure is to make it easier and less error prone to establish fine-grained access control policies based on signed code. To be able to assign different system access privileges to different units of code, Java's access control mechanism must be able to ascertain what privileges should be given to each individual piece of code. To facilitate this process, each piece of code (each class file) loaded into a 1.2 or beyond Java virtual machine is associated with a code source. The code source basically says where the code came from and who, if anyone, has vouched for the code by signing it. In the 1.2 security model, permissions (system access privileges) are assigned to code sources. Thus, if a piece of code requests access to a particular system resource, the Java virtual machine will grant the code access to that resource only if such access is a privilege associated with that code's code source.
In the 1.2 security infrastructure, an access control policy for an entire Java application is represented
by a single instance of a subclass of the abstract class java.security.Policy
.
Each application has just one Policy
object in effect at any given time. Code that has
permission can replace the current Policy
object with a new one by invoking
Policy.setPolicy()
and passing a reference to the new
Policy
object. Class loaders consult the Policy
object to help
them to decide what privileges to grant code as they import the code into the virtual machine.
A security policy is a mapping from a set of properties that characterize running code to the permissions
granted the code. In the 1.2 security infrastructure, the properties that characterize running code are
collectively called the code source. A code source is represented by a
java.security.CodeSource
object, which contains a
java.net.URL
to represent the codebase and an array of zero or more certificate
objects to represent the signers. Certificate objects are instances of subclasses of the abstract class
java.security.cert.Certificate
. A Certificate
is
an abstraction that represents a binding of a principal to a public key, and another principal (the certificate
authority mentioned previously) that vouches for that binding. The CodeSource
object contains an array of Certificate
objects, because the same code can be
signed (vouched for) by more than one party. The signatures are usually obtained from a JAR file.
All of the tools and access control infrastructure that accompanies the concrete
SecurityManager
in version 1.2 work only with certificates. None work with bare
public keys. If you don't have a certificate authority handy, you can sign your own public key with your
private key and generate a self-signed certificate. The keytool
program from the Java
2 SDK version 1.2 always generates a self-signed certificate when it generates keys. In the code signing
example given earlier in this chapter, for instance, the keytool
created not only
public/private key pairs, but also self-signed certificates for the aliases friend
and
stranger
.
A permission is represented by an instance of a subclass of the abstract class
java.security.Permission
. A permission object has three properties: a type, a
name, and an optional action. A permission's type is indicated by the name of the permission class. Some
examples of permission types are: java.io.FilePermission
,
java.net.SocketPermission
, and
java.awt.AWTPermission
. A permission's name is encapsulated inside the
Permission
object. For example, the name of a
FilePermission
might be: "/my/finances.dat"
; the name
of an SocketPermission
might be
"applets.artima.com:2000"
; and the name of an
AWTPermission
might be
"showWindowWithoutBannerWarning"
. The third property of a
Permission
object is its action. Not all permissions have an action. An example of
an action for a FilePermission
is: "read,write
", and for a
SocketPermission
is: "accept,connect
". A
FilePermission
with the name /my/finances.dat
and
action read,write
represents permission to read and write to the file
/my/finances.dat
. Both name and action are represented by
String
s.
The Java API has a large hierarchy of permissions that represent potentially dangerous actions code
may wish to take. You can also create your own permission classes to represent custom permissions that
you use for your own purposes. For example, you could create permission classes that represent permission
to access particular records of your proprietary database. Defining custom permission classes is one way
you can extend the 1.2 security mechanism to reflect your own needs. If you create your own
Permission
classes, you can use them like any of the built-in
Permission
classes from the Java API.
In the Policy
object, each CodeSource
is associated with
one or more Permission
objects. The Permission
objects
with which a CodeSource
is associated are encapsulated in an instance of a subclass
of java.security.PermissionCollection
. Class loaders can invoke
Policy.getPolicy()
to get a reference to the policy object currently in effect.
They can then invoke getPermissions()
on the Policy
object, passing in a CodeSource
to get a
PermissionCollection
of Permission
objects for the
passed CodeSource
. A class loader can then use the
PermissionCollection
retrieved from the Policy
object to
help it decide what permissions the code it is about to import will be granted.
java.security.Policy
is an abstract class. One of the implementation
details of concrete Policy
subclasses is how an instance of the subclass learns what
the policy should be. Subclasses can take various approaches, such as deserializing a previously serialized
policy object, extracting the policy from a database, or reading the policy from a file. The concrete policy
subclass supplied by Sun with the version 1.2 Java Platform takes the latter approach: it enables you to
express your security policy in a context free grammar in an ASCII policy file.
A policy file is consists of a series grant clauses, each of which grants a code source a set
of permissions. As mentioned previously, a code source consists of a codebase, which is a URL from which
code was loaded, and a set of signers. In the policy file, signers are designated with the alias with which the
signer's public key is stored in a keystore file. The keystore can be explicitly specified in the policy file in a
keystore
statement.
As an example of a policy file, consider the policyfile.txt
file from the
security/ex2
directory of the CD-ROM:
keystore "ijvmkeys"; grant signedBy "friend" { permission java.io.FilePermission "question.txt", "read"; permission java.io.FilePermission "answer.txt", "read"; }; grant signedBy "stranger" { permission java.io.FilePermission "question.txt", "read"; }; grant codeBase "file:${com.artima.ijvm.cdrom.home}/security/ex2/*" { permission java.io.FilePermission "question.txt", "read"; permission java.io.FilePermission "answer.txt", "read"; };
The first statement in the policyfile.txt
file is a
keystore
statement:
keystore "ijvmkeys";
This keystore statement indicates that the key aliases mentioned in the rest of the policy file refer to
certificates stored in a file named "ijvmkeys"
. Because this filename includes no
path, the file must be located in the current directory -- the directory in which the Java application using this
policy file is started.
The second statement in the policy file is a grant statement:
grant signedBy "friend" { permission java.io.FilePermission "question.txt", "read"; permission java.io.FilePermission "answer.txt", "read"; };
This statement grants two permissions to any code signed by the entity with the alias
"friend
". The granted permissions are: permission to read a file named
question.txt
and permission to read a file named
answer.txt
. Because these filenames appear with no path, both files must be in the
current directory, the directory in which the application is started. Because no codebase is mentioned in the
grant clause, code signed by friend
can come from any codebase. All code signed by
friend
, regardless of codebase, will be awarded permission to read
question.txt
and answer.txt
.
The third statement in policyfile.txt
is another grant statement, similar in
form to the first:
grant signedBy "stranger" { permission java.io.FilePermission "question.txt", "read"; };
This statement grants one permission to any code signed by the entity with the alias
"stranger
": permission to read a file named question.txt
.
This file must be sitting in the current directory, the directory in which the application is started. Because no
codebase is mentioned in the grant clause, code signed by stranger
can come from
any codebase and will still be awarded permission to read question.txt
. Note that
although stranger
is allowed to read the question contained in
question.txt
, stranger
is not allowed to peek at the answer
contained in answer.txt
. This contrasts with the privileges awarded to
friend
, who is allowed to read both the question and the answer.
The fourth and final statement in this policy file is yet another grant statement:
grant codeBase "file:${com.artima.ijvm.cdrom.home}/security/ex2/*" { permission java.io.FilePermission "question.txt", "read"; permission java.io.FilePermission "answer.txt", "read"; };
This final grant statement grants two permissions to any code that was loaded from a particular
directory, permission to read a file named question.txt
and permission to read a
file named answer.txt
. Both files must be in the current directory, the directory in
which the application is started. Note that this grant statement does not mention any signers. The code can
be signed by anyone or no one. So long as it is loaded from the indicated directory, the code will be granted
the listed permissions.
The codebase URL in this grant statement takes the form of a file:
URL that
includes a property, ${com.artima.ijvm.cdrom.home}
. If you run the
AccessControl
example programs described later in this chapter, you'll have to set
the com.artima.ijvm.cdrom.home
property to the path of the CD-ROM that
comes with this book, or to whatever directory you have moved the security
subdirectory from the CD-ROM. The Policy
object that is instantiated based on the
contents of policyfile.txt
will take the
com.artima.ijvm.cdrom.home
property into account when it constructs the
URL
for the CodeSource
for this grant clause.
Sponsored Links
|