Přeskočit na obsah

RBAC scope filter přepsán URL parametrem (own → all)

import { Aside } from ‘@astrojs/starlight/components’;

Uživatel s rolí, která má v RBAC scope own (smí vidět jen své záznamy), přepne v UI na testovací účet a v sekci uvidí klienty jiného uživatele. Vrátí to zpět na admin účet, zkusí znovu — stále vidí cizí. Někde v URL je ?ownerId=<id_jiného_uživatele> — ale to nemá smyslem fungovat, protože scope filter by ho měl přebít.

List endpoint (GET /api/clients, /api/leads, …) staví where klauzuli ve dvou krocích:

// 1) scope filter podle role
let where = applyScopeFilter(auth, {}, "ownerId");
// pro scope=own vrátí: { ownerId: auth.user.id }
// 2) user-input parametry
const ownerId = searchParams.get("ownerId");
if (ownerId) where.ownerId = ownerId;
// ❌ TICHÝ PŘEPIS — scope filter je k ničemu, user vidí cizí data

where.ownerId = ownerId přebije to, co tam dal applyScopeFilter. Útočník (nebo zvědavý kolega) napíše do URL ?ownerId=<jiné_id> a vidí cizí záznamy s normální 200 response. Žádná chyba v logu — vypadá to jako legitimní filtr.

Pattern je obvykle součást „filtrace dle vlastníka” v UI (admin si filtruje pohledem konkrétního OZ) — endpoint si parametr přijme, ale zapomene zkontrolovat, že volající ho má smět použít.

Centrální helper v RBAC modulu:

rbac.ts
export function violatesOwnScope(
auth: PermissionResult,
requestedUserId: string | null | undefined
): boolean {
if (!requestedUserId) return false;
return auth.scope === "own" && requestedUserId !== auth.user.id;
}

V každém list endpointu, který přijímá ?ownerId/?creatorId/?assignedToId/?managerId, kontrolovat před přiřazením do where:

const ownerId = sp.get("ownerId");
if (violatesOwnScope(auth, ownerId)) {
return NextResponse.json(
{ error: "Nemáte oprávnění filtrovat dle jiného uživatele." },
{ status: 403 }
);
}
if (ownerId) where.ownerId = ownerId; // teď bezpečně

Proč 403 a ne tiché filtrování? Tiché vrácení prázdného listu maskuje útok — admin si v logu nevšimne, že někdo zkoušel scope bypass. 403 je viditelné selhání.

Alternativa — opačné pořadí v where: aplikovat applyScopeFilter až NA KONCI, tj. po user-inputu. Pak scope filter user-input přepíše. Funguje pro „silent ignore”, ale chová se jinak (útočníkův pokus nebude vidět). Pro auditovatelnost preferuj guard variantu.

Co zkontrolovat ve svém kódu

Sekce “Co zkontrolovat ve svém kódu”

Najít všechny list endpointy, kde se nastavuje where pole, které je zároveň ownerField parametr applyScopeFilter:

Terminál
grep -rn 'where\.\(ownerId\|assignedToId\|creatorId\|managerId\|userId\)\s*=' src/app/api

Pro každý zkontrolovat pořadí + přítomnost guardu. V typickém Next.js + Prisma + custom RBAC layoutu bývá takových endpointů 6–10.

  • Souvislost s [[match-records-by-id-or-email]] — také o tom, jak může user-input parametr nevědomky obejít kontrolu.
  • IDOR (Insecure Direct Object Reference) — OWASP Top 10 A01.
Přidal aiarchitekt.cz · 30. 5. 2026 2:00
Provozuje aiarchitekt.cz