Liigu sisu juurde

Supabase RLS: kuidas kahe org-i kasutaja andmeid lekitab

Supabase

Vaata MarkdowninaAva rakenduses ClaudeAva rakenduses ChatGPTAva rakenduses Perplexity

Küsimus kopeeritakse ka. Kui vestlus avaneb tühjana, kleepige see sisse.

Lühidalt

Poliitika nagu auth_in_org(organization_id) küsib: “kas helistaja on selle rea omanud organisatsiooni (org-i) liige?”. Kasutaja puhul, kes kuulub org-i A ja org-i B, on vastus mõlemas “jah”. Minu sondis andis ainult RLS sellisele kasutajale lugemis- ja kirjutamisõiguse teise org-i dokumendile. Kui päringule lisati praeguse päringu org, tuli tulemuseks 0 rida.

Leidsin selle ehitusdokumentide platvormi kõvendamisel, mis on mitme rentnikuga Supabase’i rakendus. Igal rentniku tabelil oli row level security sisse lülitatud ja jõustatud, nii et see tundus valmis.

Mis tundus turvaline?

Kõigil poliitikatel oli sama kuju: rea organization_id peab olema org, mille liige helistaja on. Seetõttu otsisid mõned serveritoimingud dokumenti ainult id järgi. Põhjendus oli “RLS filtreerib selle niikuinii”.

const { data } = await supabase
  .from('documents')
  .select('*')
  .eq('document_id', id)
  .single()

Ühe liikmesusega kasutaja puhul see töötab ja test sellise kasutajaga läheb läbi.

Miks RLS laseb teise org-i läbi?

Poliitika on liikmesuse test. Postgres hindab seda iga rea kohta, nagu kirjeldab row security dokumentatsioon, ja see ei tea, millise organisatsiooni nimel päring tegutseb, sest see elab rakenduses: URL-is, küpsises või org-i vahetajas. Kasutaja puhul, kes on mõlema org-i liige, läbivad kontrolli mõlema org-i kõik read.

Policy ei tea, millisesse org-i päring kuulub. Üks kasutaja, org-i A ja org-i B liige, tegutseb org-is A. Ainult RLS-poliitika: Päring org-is A → Rea org-i liige? → Jah, A või B → Org-i B rida tagastati; RLS pluss selle päringu org: Päring org-is A → Rea org-i liige? → Rida org-is A? → 0 rida org-ist B.
Minu sond külvatud kasutajaga kahes org-is, september 2026: ainult RLS andis lugemis- ja kirjutamisõiguse org-i B dokumendile; päringu org-i lisamine tagastas 0 rida.

Seega ei ole rakenduse organization_id filter RLS-i peale lisatud teine kiht. Selle kasutaja jaoks on see ainus kaitse.

Kui te juba nende abifunktsioonide juures olete, vaadake, kas need arvestavad organizations.is_active. Liikmesuse abifunktsioonid sageli ei arvesta ja siis säilitab deaktiveeritud org RLS-i tasemel lugemis- ja kirjutamisõiguse. Kui ainus is_active kontroll asub rakenduse rentniku lahendajas, jätab iga päring, mis lahendajast mööda läheb, deaktiveerimise samuti vahele.

Mis selle parandas?

Päringu org sai kohustuslikuks argumendiks igas teenusefunktsioonis, mis loeb või kirjutab rentniku andmeid. Väljakutse kohal ei saa seda unustada, sest ilma selleta funktsioon ei kompileeru.

export async function getDocument(orgId: string, id: string) {
  return supabase
    .from('documents')
    .select('*')
    .eq('organization_id', orgId)
    .eq('document_id', id)
    .single()
}

Ülevaatusest tuli veel kaks reeglit:

  • Ärge kunagi võtke org-i reast, mida kavatsete kirjutada. update ... where id = $1 and organization_id = <that row's own organization_id> on alati tõene, seega ei kaitse see midagi.
  • Liitvõõrvõti (id, organization_id) peal paneb andmebaasi keelduma sidumast erinevate org-ide ridu. Kontrollige iga alamtabelit, kolmele on seda lihtne lisada ja neljas unustada.

Kuidas oma rakendust kontrollida?

Looge üks kasutaja, kellel on aktiivne liikmesus kahes org-is. Seejärel tegutsege kohaliku või staging-koopia SQL-redaktoris selle kasutajana ja küsige teise org-i rida:

begin;
set local role authenticated;
set local request.jwt.claims = '{"sub": "<user id>", "role": "authenticated"}';

-- RLS alone: returns the org B document
select document_id from documents where document_id = '<org B document id>';

-- with the org of the request: 0 rows
select document_id from documents
where document_id = '<org B document id>'
  and organization_id = '<org A id>';
rollback;

Korrake seda update-iga ja veel kord pärast teise org-i deaktiveerimist. Kui esimene päring tagastab rea, vaadake läbi kõik kohad, kus rentniku andmeid päritakse ainult id järgi.

Kui tõsine see on?

Hinnake seda ausalt. Ründajal peab olema päris liikmesus mõlemas org-is, seega on see küsimus andmete terviklikkusest ja auditijälgedest: keegi, kellel on ligipääs kahele kliendile, muudab valet klienti ja logi ütleb, et see oli lubatud. See ei ole viis, kuidas võõras saaks lugeda kõiki rentnikke.

Üldine õppetund on sama, mis kirjutises kuidas ma Supabase’i RLS-i testin: testige, mida päris kasutaja saab teha, päris andmekujudega, mitte ainult seda, mida iga poliitika ütleb. Kolm seotud märkust: registreerimise metaandmetest võetud roll, NULL, mis jättis õiguse kontrolli vahele ja millised skeemid Data API serveerib. Kahe ettevõtte test on mu Lovable’i rakenduse turvakontrollnimekirja esimene punkt. Poliitikate, õiguste ja seadete jaoks nende ümber käitab tasuta Supabase turvakontroll 46 OWASP Top 10:2025 reeglit; kas poliitika laseb kahe org-i kasutaja läbi, on andmekuju küsimus, millele vastab ainult ülaltoodud päring.

© 2026 Denys Zaichenko. Algne artikkel: https://madsalt.tech/et/notes/supabase-rls-user-in-two-organizations/