Basis Lab

Letting a Model Call Your Code Safely

The Narrow Door, and Nothing Else

Last timeThe Secrets It Must Never See

Once the privileges, the limits, the network and the secrets are gone, one hole is left on purpose. Shaping it, capping it and clocking it is what keeps the last hole from giving everything back.

What is left to design

The submission now runs as nobody, owns nothing, sees a read only file

system apart from one scratch folder, has no route off the machine, and

can read nothing in its environment worth having. It has 2 seconds of

processor time, 10 seconds of wall clock, 256 MB of memory, 64 MB of

writing and 32 processes.

And it still has to do its job, which is to be run against ten inputs so

that what it printed can be returned. That requires a hole, deliberately

left, and it is the only hole left. Everything an attacker can still try

is therefore an attack on the shape of that hole rather than on anything

the program holds by itself.

The hole has a useful smallest form. For one run the submission receives

one input, writes what it printed, and may write a diagnostic. That is

three open descriptors, numbered zero, one and two, and nothing else: no

file it named, no address it named, no job it named, no argument it chose.

FIG 1One run across the door
Twelve boxes and one loop, and the loop is the point: the collector goes round it while the program is alive rather than waiting at the end. Notice that the refusal path and the success path meet again before anything is stored, so the cleanup is not a thing a hostile run can skip by being refused.

A door is a list of what it allows

The first decision is whether the door is described by what it permits or

by what it refuses, and the two are not two styles of the same thing.

A door described by what it refuses is a general facility with a filter in

front of it. Its surface is everything the facility can do minus whatever

the filter happened to think of, and nobody can state that set. A door

described by what it permits has a surface you can write down and count,

which is the only form in which the sentence "an attacker who fully

controls this code can reach the following" has an answer.

In the smallest form above the list is: deliver one input, collect one

output, collect one diagnostic. Three operations, no arguments. Real

services grow past that, so the discipline is that each new operation is

named, takes a fixed argument list, and has every argument the submission

chose removed from it.

FIG 2Four shapes of door, by what the submission gets to name
can name a pathcan name a destinationcan reach what the runner can reachhow much it helps, 0 to 3
a shell, or anything that takes a command line1110
an operation that opens a path the submission supplied1101
a named operation whose arguments are all fixed by the runner0002
bytes in on descriptor 0, bytes out on descriptor 10003
The marked cells are the pair worth comparing. The second row looks like the fourth with one convenience added, and that one convenience is the whole difference, because naming a path is naming something the runner is allowed to open and the submission is not. The first and second rows score zero and one because an operator cannot state what they expose.

Lending out privileges the submission never had

The runner is not confined. It has to read the input store, write the

result store, and talk to whatever queue handed it the job. It therefore

holds every privilege the submission was stripped of, and it is standing

on the other side of the door.

So the dangerous question is not what the submission may do. It is which

of the runner's own powers an argument can steer. A runner that opens a

path the submission supplied is using its credentials on the submission's

instruction, and the effect on the operator is that two of the ten hostile

attempts succeed without the submission ever needing a privilege: reading

another reader's submission, and reading a file holding credentials. The

submission could not open either. The runner could, and was asked to.

This is old and it has a name. Hardy's note on the confused deputy makes

the general statement that a trusted program contributes its own authority

to whoever supplies its arguments, so the authority in play is the sum and

not the caller's share. The practical consequence is the one people resist,

because it sounds lazy: do not validate the argument, delete it.

Validation leaves the lever attached. Every validated argument is a rule

somebody has to keep correct against a decoder, a symbolic link, a

relative path and a renaming, for as long as the service exists. A door

with no such argument has no rule to keep. If the runner is going to open

input number seven, the runner decides that from the job record it was

handed, and the submission has no way to refer to a file at all.

The lesson stops here

6 more paragraphs to go

You have read the opening. The rest of the argument, the problems that check whether it landed, and the lines worth keeping at the end all come with a plan.

The first lesson of every course in the library reads the whole way through, free, so you can see exactly what the rest of them are.

See the planThe contents

This is the reading half

Starting the course gives you your own copy of it. Every idea on every page has problems standing under it, marked with a reason rather than a tick, and any sentence you do not believe can be opened and argued with. None of that can happen on a page nobody owns.

The contents

The rest of this course

  1. 01What a Submitted Program Can Actually Ask For
  2. 02Four Places the Boundary Can Go, and What Each One Stopsopening only
  3. 03Run It as Nobody, Owning Nothingopening only
  4. 04Five Ceilings, and the Twenty Cores They Implyopening only
  5. 05Give It No Route Out, Then Name the Exceptionsopening only
  6. 06Nothing in the Environment Worth Stealingopening only
  7. 07The Narrow Door, and Nothing Elseyou are here
  8. 08Ten Checks, and the One Wall That Can Still Fallopening only

Read alongside