Guider · Data og tilgang
Tilgang på radnivå, og hvorfor den hører hjemme i databasen
Nesten hver applikasjon har en regel av typen «du får bare se dine egne poster». Hvor den regelen er skrevet, avgjør om den er en garanti eller en vane.
Kortversjonen
- Tilgang på radnivå svarer på «hvilke poster», ikke «hvilke endepunkter». Det er to ulike spørsmål, og de trenger hver sin mekanisme.
- En regel som håndheves i skjermbildene eller i koden til hvert endepunkt, holder bare så lenge hver fremtidige spørring husker den. Det er et veddemål på et teams hukommelse, på ubestemt tid.
- Er regelen erklært ved dataene, gjelder den for hver spørring — også rapporten, eksportknappen og bakgrunnsjobben noen legger til neste år.
- Test den slik en angriper ville gjort: be om en post du ikke skal ha lov til å se, ved å oppgi id-en, og se hva som kommer tilbake.
Tilgang på radnivå er svaret på et spørsmål hver applikasjon med mer enn én kunde må svare på: av alle radene i denne tabellen, hvilke har akkurat denne personen lov til å røre? Ikke «får denne personen lese fakturaer» — det er et grovere spørsmål om endepunktet — men «får denne personen lese denne fakturaen».
Skillet høres pirkete ut helt til man legger merke til at de fleste datalekkasjer ikke er innbrudd. Det er vanlige, innloggede brukere som får rader som aldri var ment for dem, fra en spørring ingen tenkte to ganger over.
To spørsmål som blir forvekslet med ett
Tilgangskontroll i en vanlig applikasjon er egentlig to beslutninger lagt oppå hverandre, og de svikter på forskjellige måter.
- Tilgang på metodenivå: får denne brukeren bruke denne operasjonen i det hele tatt? «Alle innloggede brukere kan lese samlingen bookings. Bare en administrator kan slette en booking.» Dette er et ja-eller-nei-spørsmål som avgjøres før noen data leses, og det styres som regel av en rolle i brukerens påloggingstoken.
- Tilgang på radnivå: gitt at de får lese bookinger, hvilke bookinger? «De som gjelder deres eget firma.» Dette er et filter som legges på selve dataene, og det kan bare besvares ved å se på hvordan posten henger sammen med den som spør.
Team griper til roller for å løse begge deler, fordi roller er verktøyet som allerede ligger der. Det fungerer helt til den første kunden som vil at to av sine egne brukere skal se ulike deler av sine egne data — og da finner man opp én rolle per kunde, og rollelisten har blitt en treg, dårlig indeksert kopi av datamodellen.
De tre stedene regelen havner
I praksis havner regler for enkeltposter på ett av tre steder, og de er ikke likeverdige.
- I brukergrensesnittet. Skjermbildet viser bare den innloggede brukerens poster, så ingen ser noe de ikke skal. Dette er ikke sikkerhet; det er layout. Den underliggende forespørselen kan sendes på nytt for hånd med en annen id, og alt grensesnittet skjulte, kommer tilbake.
- I koden til endepunktet. Hvert endepunkt legger til en betingelse —
where company_id = current_user.company_id— før spørringen kjøres. Dette er ekte håndheving, og det virker. Men det må også gjentas i hvert endepunkt, for alltid, av alle, også av den som legger til en CSV-eksport klokka halv fem en fredag. - Ved dataene. Regelen erklæres én gang, på relasjonen som gjør en post til «din», og databasen bruker den på hver spørring mot tabellen, uansett hvilken kode som spurte. Det finnes ikke noe endepunkt som kan glemme den, fordi filteret ikke ligger i endepunktet.
Det andre alternativet er det vanlige, og det er verdt å være presis om hvorfor det skuffer. Det er ikke feil. Det er riktig den dagen det skrives, og så forvitrer det: et endepunkt som var trygt, forblir trygt, men flaten fortsetter å vokse — en rapportspørring, et adminskjermbilde, en webhook, en planlagt jobb, en KI-assistent som spør mot de samme tabellene. Hver av dem er en ny anledning til å utelate betingelsen, og ingenting i systemet protesterer når det skjer.
Regelen er en vei gjennom dataene
Grunnen til at regler for enkeltposter ikke lar seg uttrykke som roller, er at «din» nesten aldri er en egenskap ved posten. Det er en vei: denne oppgaven hører til et prosjekt, som hører til et team, som du er medlem av. Eierskapet ligger to eller tre relasjoner unna, og det endrer seg når noen begynner eller slutter.
Det er den formen en god mekanisme for tilgang på radnivå tar imot. Du peker på relasjonen som bærer eierskapet, og hver spørring mot samlingen snevres inn til postene som kan nås fra den som spør. Du beskriver datamodellen i stedet for å ramse opp tilfeller, og derfor fortsetter det å virke når modellen vokser.
Det gir også noe tilnærmingen med én rolle per kunde ikke kan gi: samme person kan ha ulike rettigheter til ulike poster. Arrangør av én turnering, vanlig medlem i den neste, og påloggingstokenet endrer seg aldri — fordi rettigheten er registrert på medlemskapet, ikke på personen.
Flerleietakerarkitektur er det samme problemet i dress
Flerleietakerarkitektur — ett kjørende system som betjener mange kunder som aldri må se hverandre — høres ut som et arkitekturvalg, og blir diskutert som et: én database per kunde, eller én database med en kolonne for leietaker. Men det andre alternativet er nøyaktig problemet over, og det første velges ofte mest for å slippe å løse det.
Én database per kunde er virkelig tryggere mot akkurat denne feilen, og den koster dere overalt ellers: migreringer som må kjøres per leietaker, sikkerhetskopier som må håndteres per leietaker, rapportering på tvers av kunder som blir et prosjekt, og en oppstartskostnad per kunde som i det stille setter en nedre grense for hvor liten kunde dere har råd til å ta inn. Holder regelen på radnivå ved dataene, slutter en delt database å være det risikable valget.
Slik finner dere ut om deres faktisk holder
Uansett hvilken mekanisme dere bruker, er dette kontrollene som er verdt å gjøre før dere lover en kunde at dataene deres er isolert. Hver av dem er en halvtimes arbeid, og de fleste finner noe første gang.
- Be om en post du ikke skal ha. Logg inn som én kunde, ta id-en til en annen kundes post, og be om den direkte. Du skal ikke få noe — ikke et tomt skjermbilde, men et tomt svar.
- Prøv alle dørene, ikke bare inngangsdøren. Listeendepunktet er som regel riktig, fordi noen har testet det. Sjekk lesing av enkeltposter, søk, eksport, rapporter, opptellinger og eventuelle sanntidsabonnementer hver for seg. Opptellinger er en gjenganger: «42 treff» avslører at det finnes rader listen skjuler.
- Sjekk skrivesiden også. Lesing får oppmerksomheten; det er endringer og slettinger som endrer andres data. Prøv å endre en post som tilhører en annen leietaker, og prøv å opprette en post som peker på en.
- Sjekk hva som skjer når koblingen mangler. En post der eierrelasjonen er tom, er ingens rad. Avhengig av mekanismen betyr det at den er usynlig for alle eller synlig for alle — og bare det ene er trygt. Finn ut hvilket dere har.
- Se hva kontrollen koster. Hvis regelen håndheves ved å slå sammen tabeller på tvers av relasjoner, kan en for vid konfigurasjon gjøre én lesing om til svært mange joins. Riktig men tregt er et problem dere møter i produksjon, ikke i testing.
Slik fungerer det på vår plattform
For ordens skyld, siden dere kanskje leser dette på nettstedet til et selskap som selger en slik plattform: på RestAPI.com er regelen på radnivå en sikkerhetspolicy som erklæres på relasjonen som bærer eierskapet, og den gjelder for hver spørring mot den samlingen. Tilgang på metodenivå blir der den hører hjemme — roller i tokenet, sjekket før noen data leses — og de to konfigureres hver for seg fordi de er ulike spørsmål.
Det som er verdt å stjele selv om dere aldri bruker plattformen vår: regelen erklæres mot datamodellen, ikke skrives inn i hvert endepunkt. Uansett hvilken database eller hvilket rammeverk dere bruker, er det den egenskapen som avgjør om isolasjonen deres er en garanti eller en vane.