[3104] in java-interest

home help back first fref pref prev next nref lref last post

Web security (was Please UNLOCK the power of Java)

daemon@ATHENA.MIT.EDU (David Hopwood)
Tue Oct 31 07:35:43 1995

From: david.hopwood@lady-margaret-hall.oxford.ac.uk (David Hopwood)
Date: 29 Oct 1995 16:53:20 -0800
Apparently-To: java-interest@java.sun.com

[cc:ed to java-interest for those not yet reading comp.lang.java]

I wrote:
>This suggests that resources should be given out on a need-to-know basis.
>Then, the user can quantify the risks involved, and any damage is limited to
>the specific files or resources the app was trusted with.

Here is a scheme that implements this style of security (it's ended up a bit
longer than I anticipated, but never mind). Comments and criticism are
welcomed.

~~~~~~~~~~

There are two distinct kinds of access: by name, and by capability.

Names include filenames, directory names, device names, class names,
protocol names, etc. Any app can make up a name, e.g. "/etc/passwd", and
request access to it (but obviously this does not always succeed).
Importing a class counts as accessing it by name.

Capabilities are handles that provide a given level of access to a resource.
Unlike names, capabilities are unforgeable; a client that possesses one must
have obtained it directly or indirectly from a trusted source. There are two
kinds of capability: references, which are only valid in one address space and
for a single execution of an app, and persistent capabilities, which don't
have these restrictions.

Names and capabilities can be converted as follows:

<pre>
       .----> Reference
      /           ^
  Name            |
      \           v
       `----> Persistent
              capability
</pre>

Converting from a name to a capability (of either kind) is strictly controlled.
Random applets from the net can only access harmless things by name (for
example, the GUI classes). Some apps are trusted to directly represent the
user (e.g. a GUI or a command-line shell), and can access any resource that
the user can. Signed apps would be given some intermediate level of access.

Converting between a persistent capability and a reference is usually allowed
(it may not be allowed if the capability was created with restrictions on its
use - see later). References point to objects and can be used directly.

This means it is possible for an app to use resources that it cannot access
by name, if it has been given a capability for them. For example, if I type
this on a command-line:

  myapp 'foobar.html'

myapp will receive a capability for foobar.html (as well as its name as a
string). The quotes '' indicate to the shell that a capability should be
passed.

In a graphical environment, resource names would be entered in trusted
controls or dialogues. The control gets a resource name from the user, and
returns both a capability and the name to the app. If the app attempts to
create a spoof control, it will not gain any information that it could not
have gained using the real one.

(NB. it is important that trusted controls should always be redrawn correctly,
and that the app should not be able to draw over them).

The default permissions for a capability, e.g. read, write, execute etc. for
files, are determined by the security context (see later) for the app that
receives it. The defaults can be overridden by the user on a case-by-case
basis, by selecting options in a trusted control or by adding extra flags on
the command line.

An app can request to upgrade a capability to a higher access level (which may
involve prompting the user for confirmation). It can also downgrade it to a
lower access level - this is useful if it wants to pass the capability to
another app that it does not trust completely.


Rationale
~~~~~~~~~

Capability systems work on the following heuristic:
Telling someone about a private resource is almost always associated with
giving them access to it.

This applies to programs as well as people; for a program to access a resource,
at some point the resource's name must have been entered. So it is an attract-
ive idea to link authorization directly with the act of specifying a name.

The problem with this is that names are forgeable. For example, the name
"/etc/passwd" is well known - I don't have to have told it to an app before
it can request access to that file. But if it was possible to guarantee that
all names were distinct in some sense, so that I have to tell the app about my
_specific_ /etc/passwd, this could potentially provide a good degree of
security.

This is precisely what the capability-passing scheme above attempts to
simulate. Whenever I enter a resource name in a secure way, the relevant app
will get a corresponding capability. It doesn't matter that the app may not
have the rights needed to resolve the name itself.

Concrete example: a recent post in comp.lang.java mentioned accessing a
serial port. Suppose this port has the resource name 'driver:com1'.

For a shell: the user types something like "myapp port='driver:com1'"
For a GUI: myapp displays a trusted control showing a set of device icons,
and the user clicks one of them.

In either case, only access to 'driver:com1', and nothing else is granted.
Note that a capability can encapsulate any object; in this case the object
might enforce the use of a particular protocol, or handle device conflicts,
or whatever. At some point code must be calling the native OS, and that code
must be trusted completely. However the levels above that can potentially
be written as user classes.

This scheme does not always protect the user from him/herself, in that a
user can type in a name (perhaps at the prompting of the app), without
necessarily understanding what it means. It would be possible to display
warning dialogues for the most serious cases, though.


Applications and applets
~~~~~~~~~~~~~~~~~~~~~~~~

In the present API the distinction between these is quite sharp. I would
prefer to see a continuum of app*s, ranging from ImageLoop-type things
to full-blown editors, equation solvers, mail programs, etc.
Running all of these in a unified framework would greatly increase
the opportunity for co-operation between them (I'll expand on this in
another article).
In the framework I'm suggesting, applets are just apps with severely
restricted rights to access resources by name. It should also be possible to
write a shell or browser as an app, without having to treat it as a special
case.


Security contexts
~~~~~~~~~~~~~~~~~

Access levels are determined by security contexts. A security context holds
any available security information about a class or running app, for
example where it came from, whether it was signed and who by, whether it
has been installed persistently in a particular directory, etc.

It also stores a security policy (which can be assigned independently for each
app or class). The security policy is responsible for interpreting the other
information in the context to decide which requests are granted. (The interface
java.lang.SecurityManager in the beta API does something similar to this,
although I think it needs to be made much more flexible).

In more detail: security contexts are associated with both classes, and
thread groups (see the class java.lang.ThreadGroup). When a shell or browser
runs a program, it starts by running a method of a particular class. The
security context for that class is used as the security context for the thread
group.

A class can make each request for a resource using either its class security
context, or the current thread group context. This is similar to the
distinction between setuid programs and normal programs in Unix (except
that it applies to individual classes instead of users, and each request
instead of all a program's requests). For example, when an app uses a
trusted GUI control, the control uses its class context to look up the name
entered by the user. If it used the thread group context, the request would
probably be denied.


Implementing persistent capabilities
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

A persistent capability must have the following properties:
- it specifies a unique resource and access level
- it cannot be forged
- it is 'just data' (a reference is not just data, because it has very
  specific rules on how it can be manipulated, and is only unforgeable if
  these rules are followed).

To meet these requirements, the capability must be chosen from a large
space of values, only a small (and unpredictable) fraction of which are
valid. Although in theory an attacker could guess the right one by luck,
this can be made arbitrarily unlikely.

One way of achieving this is to pair the resource name and access level
with a cryptographically secure random number. The run-time system
stores a table of these pairs, and checks on each access whether the correct
random number has been given. A capability can be revoked by removing
its entry from the table.
Another way is to digitally sign or encrypt the name and access level to
ensure it cannot be forged. (The choice between these is a tradeoff between
the checking time vs space for the table of pairs.)

A persistent capability can be associated with an 'acceptance condition' that
determines when it is valid (i.e. when it can be converted to a reference), and
what to do if it isn't.
For example, it is possible to create capabilities that are only valid for a
given length of time. Using the table implementation, this condition would
be stored in the table, e.g. as the predicate (current_time () < expiry_time).

These conditions don't have to be hardwired into the security system (e.g. as
user, group, world permissions are hardwired into Unix); they can be any
function of the capability, the security context (which includes the class and
app making the request), and the resource being accessed.


Secure persistent data
~~~~~~~~~~~~~~~~~~~~~~

A special case of an acceptance condition, could be to deny access to a
resource unless the requesting app is signed using a particular private key.
This is useful for controlling access to persistent data.

For an app to store persistent data, it must allocate a directory. Directories
are resources, so the app first prompts for the directory name using a trusted
dialogue. The resulting capability allows it to create the directory, and at
this point it can associate a public key with it. The app should be signed
using the corresponding private key.

Directories created in this way have quotas, and files created in them cannot
be symbolic or hard links to locations outside the directory.

Apps that are not signed with the appropriate private key cannot access the
directory by name (they may be able to access it via a trusted dialogue, i.e.
by asking the user nicely). This keeps apps partitioned from one another,
unless the user specifically requests it or the apps are signed using the same
private key.
Any app can request a list of the directories it is allowed to access by name,
but should not necessarily be able to see other directories.


Transferrability
~~~~~~~~~~~~~~~~

Since persistent capabilities are just strings of bits, they can be
duplicated, stored on disk, sent across the network, encrypted, signed, etc.
It also means they are potentially transferrable; if I email you a capability,
then all other things being equal, it will give you the same access to that
resource that I have.

For example, suppose I download an app from X, and give it a persistent
capability. Assuming the app is allowed network access, it can send this
capability back to X. X can email the result to Y, and when I download an
app from Y, it may be able to access the same resource.

This is sometimes a potential security hole, and sometimes very useful. The
question is whether trust is transitive, i.e. whether

    A trusts X with R, and X trusts Y with R => A trusts Y with R

In general this depends what the resource, R, is. If it is read-only data,
then it is inevitable that Y will be able to access it, because the data
itself is copyable.
If the resource represents a service, or allows write access to something,
this is much less clear, and should probably be decided by A.

Fortunately the framework I've described can handle either option. It is
possible to 'stamp' a capability with information about who it was originally
given to (or any other information, in fact), so that removing the stamp
invalidates the capability - one way is to encrypt or sign the capability
after adding the information.
When a request using that capability is received from some other app, the stamp
can be used to decide whether to accept it.

Note that if capabilities are being transferred over the network, ideally the
channel should be encrypted, and both ends of the connection should be
authenticated.


Summary
~~~~~~~

Access to a resource is either by name or capability.
Names are forgeable; capabilities are not.
As well as files, names include directories, classes, protocols, etc.
Capabilities are either references, or are persistent. A capability can refer
  to any object that encapsulates a resource.
A shell or GUI starts with access to all or most names (it is a trusted agent
  of the user).
Apps from the net start with access to few harmless names.
An app can gain further access rights by being granted capabilities.
A shell grants a capability when the user enters a name on the command line.
A GUI grants a capability when the user enters a name in a trusted control
  or dialogue.
There is a security context for each class, and each thread group.
When a thread group is started by calling a particular class, its security
  context is used as the thread group security context.
The security context for the current thread group is used to determine default
  access levels.
These access levels can be overridden when requesting each capability.
The access level for a capability can be upgraded or downgraded.
Persistent capabilities are implemented as (name, random key), or anything
  else that encodes the name and is unforgeable.
Access conditions can be attached to a persistent capability, limiting when
  it can be used.
Capabilities can be transferred between programs, either on the same system
  or (if persistent) over the network.
It is possible to detect when a persistent capability has been transferred, by
  stamping it with the user it was originally given to.


Although this scheme may sound complicated at first, most of the possibilities
arise from orthogonal combinations of a few basic ideas: capabilities, trusted
controls, and security contexts. For a different way of implementing
capabilities, see 'CACL: Efficient Fine-Grained Protection for Objects' in
OOPSLA (Object-oriented systems, languages and applications) 1992.

I will be implementing a prototype of this system; any help or suggestions
would be appreciated. One of the problems is that source for Sun's implement-
ation of the beta API has not yet been released (could anyone from Sun give
a date for this?).

David Hopwood
david.hopwood@lmh.ox.ac.uk
-
This message was sent to the java-interest mailing list
Info: send 'help' to java-interest-request@java.sun.com
-
This message was sent to the java-interest mailing list
Info: send 'help' to java-interest-request@java.sun.com

home help back first fref pref prev next nref lref last post