Back to Feed
article 2m read

Token check waved through any URL with "assets" in it

A new multi-tenant assistant service skips its token check for any path containing one word, and the stage right after it wakes that organization's container and forwards the request inside.

Article DepthPractice

Flow

Reworking a path exemption, in order

  1. 1Find every skip

    List each early exit, how it matches, and what runs right after it.

  2. 2Move shared files

    Serve truly public files from the app itself under one fixed prefix.

  3. 3Match exactly

    Clean up the path once, then compare the prefix, before any organization lookup.

  4. 4Check first

    Verify the cookie or token before loading secrets or starting a container.

  5. 5Test near misses

    Run the word under an API route, glued to a longer name, encoded, and with dot segments.

While reading the code for a new assistant service that serves many organizations, one line in the token check stopped me. Any request with the word "assets" anywhere in its path skips the token.

Each organization gets its own container, its own secrets and its own token. Browsers sign in with a cookie. Scripts send a token and put the organization's ID at the front of the path.

The code says why the shortcut is there. After a browser loads the page, it fetches scripts and stylesheets, and those requests don't carry the token. Fair. But a check for "contains this word" lets in a lot more than stylesheets.

What runs after the shortcut

What runs next is what makes this bad. First the service looks up the organization and loads its secrets. Then the token check runs, and the word skips it. Then the asset handler sees the same word. It wakes that organization's container if needed, passes the rest of the path inside, and sends back the answer. The later access check never runs.

So the shortcut really means: name an organization, include one word, and you reach its container with no token. Any new route with that word in its path gets the same pass. I haven't sent a live request, so I can't say what the container would hand back.

Three definitions of public

There was already a careful helper in the auth code that matched public paths exactly. Nothing used it. The entry point had its own list with the check commented out. The loose version is the one that ran, and no test touched it.

Public files that are actually public

Here's what I'd change:

  • Keep truly public files in the app itself, the same for everyone, under one fixed prefix.
  • Clean up the path once, then match that prefix exactly, before any organization lookup.
  • Check the cookie or token before you load secrets or start anything.
  • Treat a file inside an organization's container as that organization's data.
  • Test the whole chain with near-miss paths, like the word under an API route or glued to a longer name.

The exact match is small:

const isPublic = (p) => normalize(p).startsWith("/assets/");

The careful helper was already sitting in the auth code. It just wasn't the one that ran.

Keep reading

Pick up a connected idea or branch into a nearby one.

5 paths forward