Why a blocklist cannot stop a credential leak

Why a blocklist cannot stop a credential leak

Blocking the ways to print an environment is a list, and the list of ways to print an environment is not a list. What worked instead.

The first time an API token turned up in an agent transcript, the fix looked obvious. Find the command that printed it, add it to a blocklist, move on. The second time, a different command. The third time, a different mechanism entirely: a presence check written with the wrong shell expansion, which reads like "is this set?" and behaves like "print it".

After the third leak the guard was rewritten properly. Every credential was loaded into the environment of every command by one loader, so nothing needed to be sourced by hand. Then a hook enumerated the ways a shell can print its environment — the obvious commands, the export listings, the variable dumps, the indirection forms — and refused any command that used one.

It failed on schedule. The fourth leak went straight through the guard, because the set of ways to print an environment is not enumerable. An interpreter one-liner. A script's error output. A tool that logs its own configuration. A test fixture. A tarball listing. A debugging flag that echoes commands. Each of these is a new entry for the list, discovered after the fact, and the list only ever grows.

The correction is not a longer list

The correction is that the process which might print a secret never holds one. If the agent's environment contains nothing secret, there is nothing to print, and the question of how it might be printed stops mattering.

So the keys moved out. They live in one file on the host, owned by root, readable only by a small service that runs as its own user. The agent sends an ordinary HTTP request to a Unix socket with no credential in it. The service reads the caller's identity from the kernel, checks that this caller may use this key for this path, adds the credential, forwards the call, and returns the answer unchanged.

The agent's code did not get more complicated. It got simpler: the line that set the Authorization header was deleted and nothing replaced it.

What a blocklist is still for

The guard was kept, as a backstop. It costs nothing and it still catches the careless case. But it is no longer why the workspace is safe. The absence is.

Comments

Leave a comment

Ready when you are.

Tell us about your setup and a person will write back.