surrealdb: Record users can read another tenant's records through RETURN … FETCH
What fixes it
- 3.3.0surrealdb, rustGitHub
A fixed version on one release line is not the upgrade target for every line. Check your exact version and any vendor backports.
Description
A record user scoped to one namespace/database could read a record in another namespace/database on the same instance by naming it in a RETURN … FETCH.
A record user could select a foreign namespace/database, but ordinary table scans then refused them, because those scans checked a record user's scope. Resolving a record id through FETCH did not apply that check. So a record user who knew the id of a record in another tenant could read it:
USE NS ns_b DB db_b;
RETURN { one: post:secret } FETCH one; -- returns post:secret from ns_b/db_bTable-level and field-level PERMISSIONS on the fetched record were still applied, so a record in a PERMISSIONS FULL table was returned in full.
The same root cause was reported through several paths, each published as its own advisory and all fixed in 3.3.0:
- GHSA-2v9j-645m-pcrq: the
USEstatement - GHSA-vx2p-7hhm-wv62: the
Surreal-NS/Surreal-DBheaders with Basic authentication - GHSA-w5v9-jc5f-5rmc: the headers and the RPC
usemethod, for system users
Impact
What an attacker can do:
- As a record user of one tenant, read a record in another tenant on the same instance, given its record id. This works when the record's table and field
PERMISSIONSallow it to be read, such asPERMISSIONS FULL.
What it can't do:
- Discover record ids.
FETCHresolves an id the caller names; it does not scan or list the foreign table. - Reach any tenant without valid credentials for some namespace/database on the instance. This is not an unauthenticated bypass.
- Read or write a table whose relevant
PERMISSIONSclause denies the operation, includingPERMISSIONS NONE(theDEFINE TABLEdefault). - Pass IAM-gated operations such as
INFO FOR DB, or change roles. - Affect single-tenant deployments, or callers that already hold instance-wide (root) scope.
Patches
A request can no longer select a namespace/database outside the caller's authenticated level. The check applies to the USE statement, the RPC use method, and HTTP sessions built from the Surreal-NS / Surreal-DB headers. Root can select any namespace/database, and a namespace user any database in their namespace. Database and record users can select only their own namespace/database. Anonymous callers remain governed by table PERMISSIONS.
The query-time scope checks now apply to every scoped principal, not only record users. They also cover record fetches and the document write pipeline, so a read or write in a foreign tenant is refused however the session reached it.
- Versions 3.3.0 and later are not affected by this issue.
Workarounds
Users unable to patch should consider the following workarounds:
- On shared instances, use separate deployments, not namespace/database boundaries, to isolate tenants. Until you upgrade, issue namespace-, database-, and record-level credentials only to parties trusted with every tenant on the instance.
- Prefer a
PERMISSIONS WHEREclause that checks the caller's identity, such as$author a token claim, overPERMISSIONS FULL. This reduces exposure but does not restore the boundary.
References
- SurrealQL Documentation — USE
- SurrealQL Documentation — DEFINE TABLE … PERMISSIONS
- SurrealDB Documentation — Security and authentication
fix(core): enforce tenant boundary on USE / process_use(included in SurrealDB 3.3.0)fix(server): confine Surreal-NS/Surreal-DB header context to the auth level(included in SurrealDB 3.3.0)fix(core): confine record users to their own tenant on the write path(included in SurrealDB 3.3.0)fix(doc): re-verify record-auth namespace/database scope in the document pipeline(included in SurrealDB 3.3.0)
Acknowledgements
Thank you to rexpository for reporting this issue.
Text from GitHub Advisory Database
Affected versions 1
- surrealdb, rust>= 3.0.0, < 3.3.0GitHub