Skip to main content

Command Palette

Search for a command to run...

An include parameter can return objects the caller was never allowed to read

Updated
•2 min read•View as Markdown
A
Practical lessons on auth, authorization, and access control.

Plenty of REST APIs let clients expand related objects in one call, like GET /invoices/42?include=customer,notes. The handler checks that the caller can read invoice 42, then loads whatever the include list names and serializes it into the same response.

Priya is on the billing team, so she can read invoice 42. The customer it belongs to sits in an account she has no access to, and one of the notes is a private sales note. With include=customer,notes she gets both anyway. The expansion code trusts the invoice check and never runs a read check on the objects it pulls in.

Run the same read check on each expanded object that its own endpoint would run. When one fails, return null (or just the ID) for that field and keep the response shape the same either way. It also helps to give each route an allowlist of includes it accepts, so a client can't ask for relations nobody meant to expose there.

Quick test: as a limited user, fetch something you can read with every include the API supports. Then hit each expanded object's own URL directly. Anything that comes back 403 or 404 on its own but shows up through include is a missing check.