Remarks about module import

ihab.awad at gmail.com ihab.awad at gmail.com
Mon Aug 18 20:25:58 PDT 2008


On Mon, Aug 18, 2008 at 5:59 PM, Brendan Eich <brendan at mozilla.org> wrote:
> On Aug 18, 2008, at 5:25 PM, ihab.awad at gmail.com wrote:
>> Even in Caja, it's possible for one module to import another. What
>> needs to be passed down is *authority*, not the ability to execute
>> code.
>
> I was asking, I'm happy with an answer. But what's the requirement,
> exactly? Can you give an example?

I'll back into this topic, in a way. From MarkM's thesis, page 77,
caption to figure 8.2 --

  Authority is the Ability to Cause Effects. If 1) Bob has permission to talk
  to Alice, 2) Alice has permission to write /etc/passwd, and 3) Alice chooses
  to write there any text Bob asks her to, then Bob has authority to write
  /etc/passwd.

Loading a module in itself *may* cause effects (e.g., if the code is
retrieved via HTTP). Otherwise, by executing this code, the entity
importing the module may only consume memory and CPU time; it gains no
new authority that it did not have before. (And, strictly speaking,
the authority to load code should properly be granted by the importer
of a module.)

This requirement is driven by the desire to eliminate *ambient*
authority: a piece of code should not have abilities to cause effects
that have not been explicitly granted to it. For example, in the Unix
example, any process has the ambient authority to read a large subset
of the files on the system, regardless of whether these are needed for
the task at hand. To quote a classic example, when I write:

  cp x.txt y.txt

the "cp" process can attempt to read /etc/passwd and, if successful,
open a socket and send the contents to the "evil.com" server. Yet it
needed only to read "x.txt" and write "y.txt" and, were it so
confined, the damage it could cause would be limited to corrupting the
contents of "y.txt". This damage is consistent with a simple causal
model: "cp" can only damage the things I tell it to work on, and
nothing else. And finally, such damage, being narrowly constrained, is
now such that there is practically no reasonable economic or other
incentive for authoring a malicious "cp".

>From MarkM's thesis, page 16:

  The Principle of Least Authority (POLA) recommends that you
  grant each program only the authority it needs to do its job [MS03].

and this, as I hopefully motivated, is why we need to be sure that a
module gets authority only explicitly, from its invoker.

A capability is just an object reference that conveys authority.
Typically, it is an object that, directly or transitively, can cause
effects such as modifying valuable data held by some module; making
changes to stable storage; displaying information on an output device;
reading an input device; using the network; etc.

A deep vision of capability systems would be that, on a computer
system, the ability to cause effects at a primitive level --
essentially, access to hardware devices -- is owned by some powerful
module instances which attenuate that into fine-grained objects, each
of which represents some reasonable chunk (e.g., creating a connection
to a specific host, or writing to a specific rectangle of the screen,
or communicating in read-only fashion with a specific USB mass storage
device). Careful interaction between modules in the system, keeping
POLA in mind, ensures that everyone gets just the authority they need.

The wiring of capabilities can be done by module configuration, but an
important source of wiring information is the end-user. A deep
capability system exposes to the end-user the ability to divvy up
their authority between the modules to which they have access. For
example, given a "stock tracking gadget" module, I may create two
instances: one to hold my private information about what stocks I own,
and another that just displays some information I consider
interesting. The first I keep to myself, but I publish the second on
my public profile page. Importantly, the fact that these are two
instances of the same code does not grant them the ability to
communicate; since I have not wired up my private instance to anything
in my public profile, I know that my data is safe.

In fact, the classic rewriting of the "cp" example is to simply pass
it file descriptors, rather than file names:

  cp < x.txt > y.txt

Thus redefined, "cp" needs no ambient access to any files; everything
it needs is in its arguments or its Unix file descriptors, and that is
all provided by its parent process. POLA is satisfied. (The fact that
pretty much all Unices give "cp" the ambient authority to do pretty
much anything else, even under these circumstances, is, if one is to
take a polemical ocap perspective, simply a bug.)

> I thought you suggested that a few built-ins (Function, Number, not Date)
> were safe to populate in a global scope, "above" a module's apparent top
> lexical scope. A safe (immutable, I hope) top-level. Do I have that right?

Yes, that was my suggestion.

> Your post asserted that responsibility for naming a module belongs to the
> importer (requirer? ugh). That could be the whole truth, or half of it.
> Provider and requirer might both want to use distinct names.
> If requires consulted a different namespace from the property map of any
> object (especially of the global object), would that be insecure?

It depends on who gets to populate the map; more below....

> objcap research ... (sort of like I'm being inducted into a new religion).

Your point? Oh wait, here's comes the hat. Give generously. };->

> Could you expand on why it's inevitably insecure to have a module system
> with self-named modules accessible in some namespace built up by special
> forms such as Dave's module syntax?

Right, absolutely. The problem is that modules have an incentive to
lie about their names. Let's say I rely on a module called "PRand" to
generate pseudo-random numbers, and this is important for the
integrity of my algorithm. Let's also say I load a number of other
modules. I am now vulnerable to any of these other modules registering
itself under the name "PRand" and returning the number 1 every time,
thus breaking my code. While I am not vulnerable to any module I did
not load, I am now equally vulnerable to all the modules I load. Thus
(a) it is very difficult for me to reason clearly about the
composition of attacks my subordinate modules can mount; and (b) I
cannot meaningfully choose to trust one of them more than the others
since they can masquerade in this way.

Clearly, I still rely on the component I am calling "fetchModule" to
-- well -- fetch the correct module and not lie. And I *must* rely on
my parent to give me a correct "fetchModule". But, by avoiding
self-named modules, it is *possible* for me to build a "fetchModule"
that returns the right answer for the things that I care about, and is
not vulnerable to the misbehavior of stuff that it cannot control.

Ihab

-- 
Ihab A.B. Awad, Palo Alto, CA


More information about the Es-discuss mailing list