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.
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.
| can name a path | can name a destination | can reach what the runner can reach | how much it helps, 0 to 3 | |
|---|---|---|---|---|
| a shell, or anything that takes a command line | 1 | 1 | 1 | 0 |
| an operation that opens a path the submission supplied | 1 | 1 | 0 | 1 |
| a named operation whose arguments are all fixed by the runner | 0 | 0 | 0 | 2 |
| bytes in on descriptor 0, bytes out on descriptor 1 | 0 | 0 | 0 | 3 |
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 contentsThis 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