Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (5.59 MB, 702 trang )
1. Class loader
The class loader (see 2.1.2.1, “The Class Loader” on page 46 and 2.1.2.2,
“Where Class Loaders Come From” on page 47) determines how and
when Java programs can load codes, and ensures that system-level
components within the run-time environment are not replaced.
2. Class file verifier
The class file verifier (see 2.1.2.3, “The Class File Verifier” on page 47)
ensures proper formatting of downloaded code. It verifies that the
bytecode does not violate the type safety restrictions of the Java Virtual
Machine (JVM), that internal stacks cannot over/underflow, and that the
bytecode instructions will have correctly typed parameters.
3. Security manager
The security manager (see 2.1.2.4, “The Security Manager” on page 49)
performs run-time access controls on attempts to perform file I/O, network
I/O, create a new class loader, manipulate threads and thread groups,
start processes on the underlying operating system, terminate the JVM,
load non-Java libraries (native code) into the JVM, perform certain types
of windowing system operations and load certain types of classes into the
JVM. For example, the Java applet sandbox, which severely constrains
downloaded, untrusted applets to a limited set of functions that are
considered to be relatively safe, is a function of the security manager.
Java security functionalities, even if built and designed in the language itself,
have been changing their features over time, and their evolution has been
dependent on the major Java language releases that have been developed
until now: JDK 1.0, JDK 1.1 and Java 2 SDK, Standard Edition, V1.2.
3.2 Evolution of the Java Security Model
The Java programming language is one of the fastest-growing technologies in
use on the Internet today. The principal reason why Java has scored over
other languages is the promise that an application written once in Java can be
run from any machine that has a JVM. From the early stages of Java
development, it was realized that this feature poses the greatest challenge to
Java security because code distribution is risky.
3.2.1 The JDK 1.0 Sandbox Security Model
The entire focus of the initial security model provided by Version 1.0 of the
Java platform (known as the sandbox model) was to treat code downloaded
from a remote location as untrusworthy and provide a restricted environment
70
Java 2 Network Security
(the sandbox) to limit the resources that could be accessed by the alien code.
At the same time local code was considered trustworthy and was allowed full
access to all the system resources, as illustrated in the figure below:
Remote Code
Local Code
JVM
Sandbox
Resources
Figure 30. JDK 1.0 Sandbox Security Model
This was achieved by using the three components discussed in 3.1, “The
Need for Java Security” on page 69, namely the class loader, the class file
verifier and the security manager. However with the actions of the remote
code constrained to a bare minimum, the Write Once, Run Anywhere benefit
of Java could not be fully exploited.
Remote applets, though a powerful concept, were shackled by having to run
inside a sandbox, and by not being able to perform several operations. They
could not read local files and could not write to the disk. They had absolutely
no access to the system resources. Moreover they could establish a network
connection only with their servicing Web server. This heavily restricted the
use of remote applets for all but cosmetic functions to decorate a Web page.
The New Java Security Model
71
3.2.2 The Concept of Trusted Code in JDK 1.1
The next phase of evolution of Java security was based on an effort to
increase the breathing space for remote code at the client location without
compromising the safety of the client. The security architecture in JDK 1.1
introduced the concept of signed remote code. Remote codes, signed by a
trusted entity, were permitted access to several of the system resources that
were off limits for those remote programs without a trusted signature on them,
as shown in the following figure:
Local Code
Remote Code
Signed and
trusted by
the client
Unsigned/Signed
and not trusted
by the client
Sandbox
JVM
Resources
Figure 31. Trusted and Untrusted Code in JDK 1.1
A remote code (a remote applet or servlet, for example) with an appropriate
digital signature was treated with the same respect as local code, and so it
could be considered trusted. An appropriate digital signature was one that
was recognized as trusted by the client.
On the other hand, unsigned remote code or remote code signed with a
digital signature not recognized as trusted by the client, was still confined to
the sandbox.
72
Java 2 Network Security
Though this opened up interesting possibilities, the system was still rather
crude, with all local Java applications enjoying full access to the system
resources and all remotely loaded code running inside a sandbox, unless
signed by a trusted entity.
3.2.2.1 The jar and javakey Tools
Starting with JDK 1.1, the Java platform has offered the jar command line
tool to pack and deliver remote codes together with their signatures, if any, in
the Java Archive (JAR) format. The JAR file format (which we introduced in
1.4.1.3, “Packing the Applet Class in a JAR File” on page 18) is based on the
ZIP file format and is used for aggregating and compressing many files into
one. Although the jar utility can be used as a general archiving tool, the
primary motivation for its development was so that Java applets and their
requisite components (class files, images, sounds, etc.) could be downloaded
to a browser in a single HTTP transaction, rather than opening a new
connection for each piece. This greatly improves the speed with which an
applet can be loaded onto a Web page and begin functioning. The JAR
format, like a ZIP format, also supports compression, which reduces the size
of the file and improves download time still further. Additionally, a JAR file
may be digitally signed by the applet authors to authenticate the origin of the
code.
The JAR format is cross-platform, handles audio and image files as well as
class files and is backward-compatible with existing applet code. JAR
consists of a ZIP archive, as defined by PKWARE, containing a manifest file
and potentially signature files (see http://www.pkware.com). The jar tool is
basically a Java application that combines multiple files into a single JAR file.
JDK 1.1 offered the javakey tool to sign JAR files.
3.2.2.2 JDK 1.1 Security API
The Java security API was built around the java.security package and its
subpackages java.security.acl and java.security.interfaces. The first release
for Java security, available in JDK 1.1, included primarily cryptography
functions, which could be incorporated into Java-based applications. The
cryptography framework in the Java security API was designed so that a new
algorithm could be added later on without much difficulty and could be used in
the same fashion as existing algorithms. For example, even if Digital
Signature Algorithm (DSA) was the only built-in algorithm in this release, it
was possible to use software from providers to help generate RSA signatures
and key pairs for encryption.
The New Java Security Model
73
The first release of Java security in JDK 1.1 included APIs for digital
signatures, message digests, key management and access control lists
(ACLs). APIs for data encryption and other functionalities, together with their
implementations, were released separately in the Java Cryptography
Extension (JCE) 1.1 as an add-on package to JDK, in accordance with United
States export control regulations (see 2.2.3, “United States Export Rules for
Encryption” on page 57). The JCE APIs included block and stream cipher,
symmetric and asymmetric encryption and support for multiple modes of
operation and multiple encryption.
3.2.3 The Fine-Grained Access Control of Java 2
An obvious handicap with the JDK 1.1 security architecture was no easy way
of achieving fine-grained access control, with all local code enjoying
unrestricted access to all the system resources and all remote code
subjected to sandbox constraints unless signed in a way recognizable to the
client as trusted. By fine-grained access control, we mean the ability to grant
specific permissions to a particular piece of code about accessing specific
resources of the client (say read and write permission on file x, but only read
permissions on file y and no permissions on file z) depending on the signers
of the code and/or the URL location from which the code was loaded. Thus,
existence of a fine-grained access control would allow a user to specify
access permissions on a case-by-case basis rather than a rigid classification
of local code being fully trusted and remote code being untrusted and
restricted to a sandbox, unless signed in a way recognizable to the client as
fully trusted.
The new security architecture developed in Java 2 allows easy fine tuning of
the access controls. The concept of signed code can now be extended to
local code as well. With the new security model, all code, whether remotely
downloaded or local, signed or unsigned, will have access to system
resources based on what is defined in a policy file. This allows the client to
explicitly specify the permissions to be granted to different signatories of code
and different sources. This way the end user can download, install and run
applications from the Web by granting them permissions for only those
actions that are necessary. This will eliminate codes that have a hidden
agenda, such as letting you play a nice game while sending your credit card
information or your password file to a particular server at the same time.
Consider for example the following scenario, based on the JDK 1.1 security
model. You download a little tic-tac-toe program from the Web. It is signed by
an entity you trust, and you are sure that it will not crash your system. For this
reason, you accept to run it. Nonetheless, this code reads your address book,
and sends all the e-mail addresses you have to the database of the nearest
74
Java 2 Network Security
junk mailer. Though not very malicious, this is something we all would like to
avoid. This is a very likely situation, since more and more software is just
being brought off the net, and this trend is likely to continue for a long time.
This might lead to fly-by-night software vendors, some of whom might come
up with very innovative software, but some of whom you cannot really trust.
With JDK 1.1, you do not have an option to restrict access to code to do only
certain things. You either install the software, or you make do without it.
However, if you are running Java 2-enabled software, you can instruct the
JVM, through modifications in a policy file, that code loaded from a particular
URL (local or remote) and/or signed by a particular entity is restricted to
specific local resources. For example, you may specify in the policy file that
the code in question may read files in one particular directory and can do
nothing else – cannot open sockets, cannot write or delete any files, etc. This
is the fine-grained control mechanism offered by Java 2. For more on this, see
1.4, “Understanding Java 2 Security” on page 12.
In versions of Java prior to Java 2, the JVM resource access was enforced by
the sandbox security model, which was a function of the security manager.
Extensions were usually limited to features implemented by the platform
providers such as Web browsers and Web servers. When using Java 2, you
can have full control over what each of your programs and applications is
permitted to do – this was never possible until now. Similarly, you can now
define the exact things an applet coming from a particular URL can do, or
what any programs (applets, applications, servlets) signed by one or more
particular entities can do. Further, in multi-user systems, the system
administrator can define a default system policy, and each of the users of the
system can have their own policy, which is combined with the system default.
Java programs now have the ability to define access restrictions on sensitive
resources without having to write a new security manager or modify the
underlying platform. For example, applets downloaded into a Java 2-enabled
Web browser and servlets downloaded into a Java 2-enabled Web server can
add resource access controls to a JVM without having to modify the
underlying browser or server implementation. These new concepts of
permission and policy enable the Java 2 platform to offer fine-grained, highly
configurable, flexible, and extensible access control.
The Java 2 security model has been depicted in Figure 32 on page 76. As
seen in the figure, a predetermined security policy of the client decides the
security domains within which a specific piece of local or remote code can
reside:
The New Java Security Model
75
Local or remote code
(signed or unsigned)
Security
Policy
Domain A
Domain B
Sandbox
Domain C
JVM
Resources
Figure 32. Fine-Grained Access Control Mechanism in Java 2 SDK
3.2.3.1 Lexical Scoping of Privilege Modifications
A new security feature implemented for the first time in Java 2 is the lexical
scoping of privilege modification, which is a technique enforcing the least
privileged mode. Using this technique, it is possible to enable only the
execution of the piece of code that needs the privilege. All the sensitive code
could therefore be added at one place and defined as privileged, by calling
the doPrivileged() method, belonging to the java.security.AccessController
class. The doPrivileged() method is discussed in 3.5.1, “Run-Time Access
Controls” on page 91. But to get an idea in advance, basically, through the
use of this method, Java 2 provides a facility to mark Java code as being
privileged and temporarily grant it some permissions that it normally would
76
Java 2 Network Security
not enjoy by itself by virtue of its location of origin and the identity of its
signers.
3.2.3.2 Java 2 Security Tools
Java 2 provides four powerful security tools for ensuring confidentiality,
integrity, authenticity of data and adequate control on access to various
system and non-system resources. These are jar, keytool, jarsigner and
Policy Tool.
The jar function is similar in Java 2 to what it was in JDK 1.1 (see 3.2.2.1,
“The jar and javakey Tools” on page 73). JAR files acquire specific
significance, since the old javakey, and its newer version jarsigner, can sign
only JAR files.
The keytool command line utility creates key pairs – pairs of public and
private keys – imports and exports X.509 V1, V2 and V3 certificates (see
Appendix C, “X.509 Certificates” on page 649), generates self-signed X.509
V1 certificates and manages keystores. A keystore is a protected database
that holds private keys as well as public keys and certificates. In the default
implementation, a keystore is protected using a password and each private
key stored in the keystore is protected with a possibly different password. The
private keys are used to digitally sign applications and applets whereas public
keys are used to verify signed data, and certificates are used to verify
whether a public key indeed belongs to the person it is supposed to belong.
The jarsigner command line tool signs JAR files and verifies the signature(s)
of signed JAR files. It accesses the keystore when it needs to find:
• A private key when signing a JAR file
• A public key when verifying a signature
• A certificate when verifying a public key
In the Java 2 platform, the keytool and jarsigner command line utilities
replace the JDK 1.1 tool javakey. The javakey tool had several shortcomings,
the most significant of them being the fact that both the public and private
keys were stored in the same, unprotected location (often called an identity
database). This allowed anyone with access to the identity database to
determine all keys that were stored in the file. In contrast, private keys are
now password protected in the keystore.
The Policy Tool utility, which is launched through the policytool command,
creates and modifies the external policy configuration files that define the
client’s security policy.
The New Java Security Model
77
All of these tools are discussed in detail later in this book (see Chapter 9,
“Java 2 SDK Security Tools” on page 259).
3.2.3.3 Java 2 Security API
In Java 2 two new subpackages have been added to the java.security
package, and they are java.security.cert and java.security.spec. These
packages offer more features to deal with X.509 certificates and to create
certificate revocation lists (CRLs) and certificate signing requests (CSRs). In
particular, java.security.Certificate, that in JDK 1.1 was an interface of
abstract methods for managing an identity certificate, is completely
deprecated in Java 2, which offers the entire package java.security.cert to
handle certificates. Moreover, the package java.security.cert adds X.509 V3
support to certificates.
Java 2 also provides an additional certificate interface: the X509Extension
interface in the java.security.cert package. This is an interface for X.509
extensions. The extensions defined for X.509 V3 certificates and V2 CRLs
provide methods for associating additional attributes with users or public
keys, for managing the certification hierarchy, and for managing CRL
distribution.
3.2.4 A Comparison of the Three Java Security Models
Table 1 on page 79 shows a comparison of the three Java security models
based on seven parameters, which are:
• Resource access to local unsigned code
This refers to the options provided by the security architecture to a client
to determine access to local resources for local unsigned code.
• Resource access to local signed code
This refers to the options provided by the security architecture to a client
to determine access to local resources for local signed code.
• Resource access to remote unsigned code
This refers to the options provided by the security architecture to a client
to determine access to local resources for remote unsigned code.
• Resource access to remote signed code
This refers to the options provided by the security architecture to a client
to determine access to local resources for remote signed code.
• Lexical scoping of privilege modification
This refers to the availability of the option in the security architecture to
temporarily grant more privileges to a specific piece of code in an
78
Java 2 Network Security
execution thread, which are additional to the privileges the code would
have enjoyed by itself. This facility is available only with Java 2 and
achieved with the help of the doPrivileged() method introduced in 3.2.3.1,
“Lexical Scoping of Privilege Modifications” on page 76. This method
actually internally modifies the way the run-time stack (for an execution
thread) is checked for permissions.
• Cryptographic services for data confidentiality/integrity
This refers to the availability of cryptographic services for data
confidentiality and integrity. Such services became available only with JDK
1.1.
• Digital signature services for code signing
This refers to the facility of digital signature services for signing code.
Such services became available only with JDK 1.1.
Table 1. Evolution of the Java Security Model
JDK 1.0
JDK 1.1
Java 2 SDK
Local unsigned code
resource access
Unconstrained
Unconstrained
Policy based
Local signed code
resource access
Not available
Unconstrained if trusted
Policy based
Remote unsigned code
resource access
Constrained by the Java
sandbox
Constrained by the Java
sandbox
Policy based
Remote signed code
resource access
Not available
Unconstrained if trusted
Policy based
Lexical scoping of
privilege modification
Not available
Not available
Stack annotation based
with doPrivileged()
Cryptographic
services for data
confidentiality/integrity
Not available
Java Cryptography
Extension 1.1
Java Cryptography
Extension 1.2
Digital signature
services for code
signing
Not available
Java Cryptography
Architecture DSA
signature
Java Cryptography
Architecture DSA
signature
Constrained by the Java
sandbox if untrusted
Constrained by the Java
sandbox if untrusted
This comparison shows the increasing flexibility and functionality provided by
the evolving Java security model in determining a security policy.
The New Java Security Model
79
3.3 Java 2 Protection Domain and Permissions Model
This section explains the concepts of protection domain, code source and
security policy file which are the foundations of the new security model.
A protection domain can be scoped by a set of objects that are currently
directly accessible by a principal, where a principal is an entity in the
computer system to which permissions are granted. A principal can access
objects in the protection domain by virtue of the permissions it enjoys over
the objects in the protection domain. These permissions are specified
explicitly in a security policy file, which is a text file that can be edited
manually or through the Policy Tool. The Java 2 security architecture allows
the combination of a system security policy, defined by the system
administrator, with one or more user-defined security policies. A default
system policy file comes with the installation of the Java 2 SDK (see 3.6, “The
Policy File” on page 93).
Notice that, even if an arbitrary number of policy files can be specified, there
is only one policy (meaning, one set of protection domains) in effect for the
JVM at any given time. That policy might be the result of processing the
information from many policy files. The default policy implementation, via the
java.security.Policy class, has a public refresh() method that can be used to
re-init the policy, eventually re-reading the policy file(s). However, there is no
automatic policy change: refresh() must be called explicitly.
Using this security model, it is possible to grant specific access permissions
to specific code whether local or remote. Local or remote code is now
identified by its code source. The code source for a code is a combination of
the URL location from which the code is loaded and the entity or entities that
signed the code originating from that location. The code source is
represented by the java.security.CodeSource class. The location from which
the code is loaded is passed as an argument to the constructor of the
CodeSource class in the form of a java.net.URL object. The identity of the
signer(s) is passed as the second argument to the constructor of the
CodeSource object in the form of a set of java.security.cert.Certificate
objects. These certificates are for the public keys corresponding to the private
keys that signed the code. The constructor of the CodeSource class therefore
looks like the following line:
public CodeSource(URL url, Certificate[] certs)
The location from which the code is loaded is referred to as the code base in
the policy file, as we have seen in the examples of 1.4, “Understanding Java 2
Security” on page 12. In the Java 2 security model, a policy file serves as a
80
Java 2 Network Security