Testing Tenant Isolation in AI APIs with Burp and JavaScript

Testing Tenant Isolation in AI APIs with Burp and JavaScript

pr0h0
ai-apissecurity-testingburp-suitejavascript
AI Usage (88%)

When I test AI APIs, I do not start with model quality. I start with tenant boundaries. If two workspaces can see each other's data, the model just becomes a very efficient way to surface a normal authorization bug.

That is the failure mode this post focuses on: cross-tenant reads, writes, and exports in APIs behind a chat or assistant UI. The UI often looks clean. The backend is where isolation either holds or falls apart.

Why Tenant Isolation Fails in AI APIs

AI products usually mix ordinary application data with model-facing context: conversations, files, tool outputs, embeddings, workspace settings, and audit logs. The bug is rarely “the model leaked something.” More often, the API trusted a tenant identifier from the wrong place.

A common pattern is:

  • the browser sends workspaceId
  • the server accepts it without binding it to the authenticated session
  • a later endpoint uses that ID to fetch messages, files, or vector search results

If the response is useful to the attacker, the problem is real even when the UI tries to hide it.

What to Verify Before You Trust an API Response

Tenant IDs, workspace IDs, and hidden context

Check every identifier that selects tenant-scoped data:

  • tenantId
  • workspaceId
  • projectId
  • orgId
  • conversationId
  • documentId

Do not assume only the visible request body matters. I have seen tenant context arrive in headers, cookies, query strings, and server-side session state in the same product. That mixed design is where mistakes show up.

Mixed sources of truth between client and server

A secure API should have one source of truth for who owns a record. If the client sends workspaceId, the server should treat it as a hint at best and re-derive authorization from the authenticated account.

⚠️

If the API checks only that an ID exists, you are testing lookup behavior, not authorization.

Setting Up a Safe Burp and JavaScript Test Harness

Capturing requests and replaying them with controlled changes

Burp is useful here because you want to compare requests, not brute-force them. Capture a normal workflow from Account A and Account B, then replay the same request with only one field changed.

A simple approach:

  1. Log in as Account A.
  2. Trigger a normal read, update, or export request.
  3. Send the request to Repeater.
  4. Swap only the tenant-scoping value.
  5. Compare the response status, body, and timing.

You are looking for inconsistent authorization, not crashes.

replay-request.http
POST /api/workspaces/list HTTP/1.1
Host: app.example
Authorization: Bearer <token-a>
Content-Type: application/json

{"workspaceId":"ws_a_123","limit":20}

Using JavaScript to compare responses across accounts

For repeatable checks, I like a tiny Node script that sends the same request with two different sessions and diffs the shape of the response.

const fetch = global.fetch;

async function req(token, workspaceId) {
  const res = await fetch("https://app.example/api/workspaces/list", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${token}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ workspaceId, limit: 20 }),
  });

  return { status: res.status, text: await res.text() };
}

const a = await req(process.env.TOKEN_A, "ws_a_123");
const b = await req(process.env.TOKEN_B, "ws_a_123");

console.log({ a: a.status, b: b.status });
console.log(a.text.slice(0, 500));
console.log(b.text.slice(0, 500));

This is not about exploiting anything fancy. It is about proving whether the backend uses the authenticated principal or just the submitted identifier.

Practical Isolation Tests That Catch Real Bugs

Swap identifiers and watch for cross-tenant reads

Start with read paths. They are usually the easiest place to catch a broken boundary:

  • list conversations from another workspace
  • fetch a document by ID from another tenant
  • retrieve embeddings or search results for foreign data

A 403 is good. A 404 can be fine if it is consistent. A 200 with foreign content is the bug.

Test create, update, delete, and export paths separately

Do not stop at reads. I have seen systems protect GET and forget POST /export, PATCH /settings, or DELETE /conversation/:id.

Path typeWhat to testCommon failure
Readforeign IDs and list endpointsleaked data
Writeupdate another tenant's objectcross-tenant mutation
Deleteremove foreign recordsdestructive access
Exportdownload workspace databulk exfiltration

Check model-related endpoints, not just normal CRUD routes

AI APIs often add special routes like:

  • /chat
  • /tool-call
  • /files/upload
  • /embeddings
  • /exports
  • /runs/:id

These endpoints may use different middleware than standard CRUD routes. That split is where enforcement gets uneven.

Reading the Results Without Guessing

What strong isolation looks like

Strong isolation has boring behavior:

  • foreign IDs return nothing useful
  • response shape stays consistent
  • error messages do not reveal existence
  • audit logs show the denied access attempt

If the server binds data to the session and ignores client-supplied tenant context, you usually see that consistency everywhere.

Signs of partial enforcement or UI-only protection

Red flags:

  • the UI hides another workspace, but direct API calls still return it
  • list endpoints are protected, but detail endpoints are not
  • exports work even when reads are blocked
  • some endpoints use workspaceId from the token, others trust the body

That last one is especially common. Mixed enforcement makes bugs look random until you map every route.

Fixes That Belong on the Backend

Server-side tenant binding and authorization checks

The fix is not “hide the field in the frontend.” The fix is:

  • derive tenant membership from the authenticated session
  • verify object ownership on every request
  • reject cross-tenant references before touching the datastore
  • apply the same check to read, write, export, and model-tool routes

If you use shared services or async jobs, carry the authenticated tenant context through the job boundary and re-check it at execution time.

Safer logging, error handling, and audit trails

Logs should help you investigate abuse without leaking tenant data. Keep them structured, but do not print raw secrets, document bodies, or full model inputs.

A good audit trail should answer:

  • who tried to access the object
  • which tenant they belonged to
  • which object was targeted
  • whether the request was denied or allowed

That makes incident review much easier when a boundary slips.

Conclusion

Tenant isolation bugs in AI APIs are usually ordinary authorization bugs with a larger blast radius. Burp helps you replay the same action with surgical changes. JavaScript helps you automate the comparison across accounts.

If a workspace boundary matters to the product, test it on every route that can read, write, export, or feed model context. The model is not the weak point. The trust boundary is.

Share this post

More posts

Comments