Security Advisory: Organisation Context (orgId) Accepted from Client-Controlled Cookie/Header
Security Advisory: Organisation Context (orgId) Accepted from Client-Controlled Cookie/Header
Version: 1.0.41
Severity: High
Class: Broken Object-Level Authorisation (BOLA) / Insecure cookie handling
Affected file: src/lib/trpc/trpc.ts
Overview
A security issue was identified in the way organisation identity (orgId) is resolved and trusted within the tRPC server context. Because orgId could be supplied directly by the client — both through an x-org-id cookie/header and through a procedure input override — the membership verification check could be manipulated to target organisations the attacker does not legitimately administer.
Additionally, the x-org-id cookie was being written client-side without the Secure flag on some code paths, weakening its integrity over non-HTTPS connections.
Technical Details
1. orgId Resolved from Client-Supplied Cookie/Header
The tRPC context (src/lib/trpc/trpc.ts) reads orgId directly from the incoming x-org-id cookie or HTTP header:
// Simplified illustration of the vulnerable pattern
const orgId = req.cookies['x-org-id'] ?? req.headers['x-org-id'];
ctx.orgId = orgId; // fully client-controlled
Because this value is set by the client, it cannot be trusted as a canonical authority for which organisation the session belongs to.
2. input.orgId Override in orgProcedure
orgProcedure — the tRPC middleware responsible for verifying organisation membership — also accepted input.orgId and used it to override the context-level orgId:
// Simplified illustration of the vulnerable override pattern
const resolvedOrgId = input.orgId ?? ctx.orgId;
await verifyMembership(userId, resolvedOrgId);
The membership check ran against the overridden value. Since input.orgId is entirely user-controlled, a user who is a legitimate member of at least one organisation could supply any arbitrary orgId through the procedure input and have it accepted.
3. Secure Flag Missing on Client-Side Cookie Assignment
In src/app/onboarding/page.tsx, the x-org-id cookie was set via document.cookie on the client:
// Vulnerable client-side cookie write (no Secure flag)
document.cookie = `x-org-id=${orgId}; path=/`;
Without the Secure attribute, this cookie can be transmitted over unencrypted HTTP connections, where it is visible to network observers.
Impact
- A authenticated user who belongs to any organisation in the system could potentially read or write data belonging to other organisations by supplying a different
orgIdvia procedure inputs. - On deployments not enforcing HTTPS end-to-end, the
x-org-idcookie value could be intercepted in transit.
Remediation
Required Changes
src/lib/trpc/trpc.ts
- Remove the
input.orgIdoverride. TheorgIdused for authorisation must come solely from the server-resolved context — never from procedure input fields. - Derive
orgIdfrom a server-setHttpOnlycookie only. The context should reject anyorgIdthat did not originate from a cookie written by the server.
// Recommended pattern
// orgId is read ONLY from a server-set HttpOnly cookie — never from input
const resolvedOrgId = ctx.orgId; // set server-side, not overridable
await verifyMembership(userId, resolvedOrgId);
Cookie Attributes
The x-org-id cookie must be set server-side (e.g. in a Next.js API route or middleware) with the following attributes:
Set-Cookie: x-org-id=<value>; Path=/; HttpOnly; Secure; SameSite=Lax
| Attribute | Purpose |
|---|---|
HttpOnly | Prevents JavaScript (document.cookie) from reading the value, mitigating XSS-based theft |
Secure | Ensures the cookie is only transmitted over HTTPS |
SameSite=Lax | Reduces cross-site request forgery risk |
src/app/onboarding/page.tsx
Remove the document.cookie assignment for x-org-id. Replace it with a server-side API call (e.g. a POST to a Next.js route handler) that sets the cookie via a Set-Cookie response header with the attributes above.
Action Required for Self-Hosted Deployments
- Upgrade to v1.0.41 or later.
- Ensure all application traffic is served exclusively over HTTPS so that
Securecookie attributes are enforced. - Review any custom tRPC procedures for patterns where
input.*values are used to resolve the active organisation — these must be removed. - Audit
document.cookieusage across the front-end codebase and replace any security-sensitive cookie writes with server-sideSet-Cookieheaders.