Cross-tenant data access via unvalidated organisation context
Affected
GET|PATCH|DELETE /v1/organisations/{id}/* — 23 endpoints across the administrative API.
Description
Administrative API endpoints accept an organisation identifier as a path parameter and use it directly in the data-layer query. The application confirms that the caller holds an administrator role, but never confirms that the caller administers the organisation being requested. Any authenticated administrator of any tenant can therefore operate against every other tenant by changing one value in the URL.
Organisation identifiers are sequential integers, so enumerating the full customer list required no guessing. During testing the identifier space was walked from 1 to 500 in under two minutes.
Impact
Complete loss of tenant isolation—the security property a multi-tenant SaaS platform exists to provide. An attacker with any administrator account, including one created through a self-service trial, could read every customer's user records, billing details, and uploaded documents, and modify or delete them. For a platform handling regulated data this is a reportable breach of confidentiality and integrity across the entire customer base simultaneously.
Evidence
Authenticated as an administrator of test organisation 412, requesting an unrelated organisation:
GET /v1/organisations/87/members HTTP/1.1
Host: api-staging.████████.com
Authorization: Bearer eyJhbGciOi████████████████
HTTP/1.1 200 OK
Content-Type: application/json
{
"organisation_id": 87,
"name": "████████ Logistics",
"members": [
{ "id": 9051, "email": "████████@████████.com", "role": "org_admin" },
{ "id": 9052, "email": "████████@████████.com", "role": "member" },
... 34 further records omitted
]
}
The same substitution succeeded against PATCH and DELETE
routes. We verified write access by renaming a second test organisation created for
this purpose, and made no modifications to any record we did not own.
Reproduction
- Authenticate as an administrator of any organisation and capture the bearer token.
- Note your own organisation identifier from
GET /v1/me. - Issue
GET /v1/organisations/{other_id}/memberswith any other identifier. - Observe a
200 OKcontaining another tenant's records.
Remediation
- Derive tenant context from the session, never from the request. The organisation a caller may act on should be resolved from their authenticated identity server-side; it should not be an input the client supplies.
- Where an identifier must appear in the path, enforce a membership check in a single shared authorisation middleware that every route inherits by default, rather than per-handler checks that can be omitted on new endpoints.
- Replace sequential integer identifiers with unguessable values (UUIDv4 or similar). This is defence in depth, not a fix—it raises the cost of enumeration but does not restore isolation on its own.
-
Add an automated test asserting that a member of tenant A receives
403for every route parameterised by tenant B, so regressions surface in CI.
ASVS 5.0 mapping
Verification requirements this finding fails:
- 8.2.2 (L1) — data-specific access must be restricted to consumers with explicit permissions to those data items, mitigating IDOR and broken object level authorization.
- 8.3.1 (L1) — authorization rules must be enforced at a trusted service layer, not by controls an untrusted consumer can manipulate.
- 8.1.1 (L1) — authorization documentation must define the rules restricting function-level and data-specific access by consumer permissions and resource attributes.