Implementer sikkerhet på radnivå
Sikkerhet på radnivå (RLS) bruker ikke kryptering og fungerer på databasenivå for å begrense tilgangen til en tabell ved hjelp av en sikkerhetspolicy basert på gruppemedlemskap eller autorisasjonskontekst. Dette tilsvarer funksjonelt en WHERE setning.
Sikkerhetspolicyen aktiverer en innebygd tabellverdifunksjon for å beskytte tilgangen til radene i en tabell.
Avhengig av attributtet til en bruker, bestemmer predikatet om brukeren har tilgang til den relevante informasjonen. Når du kjører en spørring mot en tabell, bruker sikkerhetspolicyen predikatfunksjonen. Avhengig av forretningskravene kan RLS være så enkelt som WHERE CustomerId = 29 eller så komplekst som nødvendig.
Det finnes to typer sikkerhetspolicyer som støttes av sikkerhet på radnivå:
Filtrer predikater – begrens datatilgang som bryter med predikatet.
Access Definisjon VELG Kan ikke vise rader som er filtrert. OPPDATER Kan ikke oppdatere rader som er filtrert. SLETT Kan ikke slette rader som er filtrert. SETT INN Ikke aktuelt. Blokker predikater – begrens dataendringer som bryter med predikatet.
Access Definisjon ETTER INNSETTING Hindrer brukere i å sette inn rader med verdier som bryter predikatet. ETTER OPPDATERING Hindrer brukere i å oppdatere rader til verdier som bryter med predikatet. FØR OPPDATERING Hindrer brukere i å oppdatere rader som bryter predikatet. FØR SLETT Blokkerer sletteoperasjoner hvis raden bryter med predikatet.
Fordi tilgangskontroll er konfigurert og brukt på databasenivå, er programendringer minimale – om noen. Brukere kan også ha direkte tilgang til tabellene og kan spørre sine egne data.
Sikkerhet på radnivå implementeres i tre hovedtrinn:
- Opprett brukerne eller gruppene du vil isolere tilgangen til.
- Opprett den innebygde tabellverdifunksjonen som filtrerer resultatene basert på predikatet som er definert.
- Opprett en sikkerhetspolicy for tabellen, og tilordne funksjonen som ble opprettet tidligere.
Følgende T-SQL-kommandoer viser hvordan du bruker RLS i et scenario der brukertilgang er adskilt etter leier:
-- Create supporting objects for this example
CREATE TABLE [Sales] (SalesID INT,
ProductID INT,
TenantName NVARCHAR(10),
OrderQtd INT,
UnitPrice MONEY)
GO
INSERT INTO [Sales] VALUES (1, 3, 'Tenant1', 5, 10.00);
INSERT INTO [Sales] VALUES (2, 4, 'Tenant1', 2, 57.00);
INSERT INTO [Sales] VALUES (3, 7, 'Tenant1', 4, 23.00);
INSERT INTO [Sales] VALUES (4, 2, 'Tenant2', 2, 91.00);
INSERT INTO [Sales] VALUES (5, 9, 'Tenant3', 5, 80.00);
INSERT INTO [Sales] VALUES (6, 1, 'Tenant3', 5, 35.00);
INSERT INTO [Sales] VALUES (7, 3, 'Tenant4', 8, 11.00);
-- View all the rows in the table
SELECT * FROM Sales;
Deretter oppretter du brukerne og gir dem tilgang til Salg-tabellen . I dette eksemplet er hver bruker ansvarlig for en bestemt leier. TenantAdmin-brukeren har tilgang til å se data fra alle leiere.
CREATE USER [TenantAdmin] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant1] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant2] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant3] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant4] WITH PASSWORD = '<strong password>'
GO
GRANT SELECT ON [Sales] TO [TenantAdmin]
GO
GRANT SELECT ON [Sales] TO [Tenant1]
GO
GRANT SELECT ON [Sales] TO [Tenant2]
GO
GRANT SELECT ON [Sales] TO [Tenant3]
GO
GRANT SELECT ON [Sales] TO [Tenant4]
GO
Deretter oppretter vi et nytt skjema, en innebygd tabellverdifunksjon og gir brukeren tilgang til den nye funksjonen. Predikatet WHERE @TenantName = USER_NAME() OR USER_NAME() = 'TenantAdmin' evaluerer om brukernavnet som kjører spørringen samsvarer med TenantName kolonneverdier.
CREATE SCHEMA sec;
GO
--Create the filter predicate
CREATE FUNCTION sec.tvf_SecurityPredicatebyTenant(@TenantName AS NVARCHAR(10))
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN SELECT 1 AS result
WHERE @TenantName = USER_NAME() OR USER_NAME() = 'TenantAdmin';
GO
--Grant users access to inline table-valued function
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [TenantAdmin]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant1]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant2]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant3]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant4]
GO
--Create security policy and add the filter predicate
CREATE SECURITY POLICY sec.SalesPolicy
ADD FILTER PREDICATE sec.tvf_SecurityPredicatebyTenant(TenantName) ON [dbo].[Sales]
WITH (STATE = ON);
GO
På dette tidspunktet er vi klare til å teste tilgangen:
EXECUTE AS USER = 'TenantAdmin';
SELECT * FROM dbo.Sales;
REVERT;
EXECUTE AS USER = 'Tenant1';
SELECT * FROM dbo.Sales;
REVERT;
EXECUTE AS USER = 'Tenant2';
SELECT * FROM dbo.Sales;
REVERT;
EXECUTE AS USER = 'Tenant3';
SELECT * FROM dbo.Sales;
REVERT;
EXECUTE AS USER = 'Tenant4';
SELECT * FROM dbo.Sales;
REVERT;
TenantAdmin-brukeren skal se alle radene. Brukerne av Tenant1, Tenant2, Tenant3 og Tenant4 skal bare se sine egne rader.
Hvis du endrer sikkerhetspolicyen med WITH (STATE = OFF);, ser du at brukerne ser alle radene.
Merk deg
Det er en risiko for informasjonslekkasje hvis en angriper skriver en spørring med en spesiallaget WHERE setning og for eksempel en divisjon-for-null-feil, for å tvinge frem et unntak hvis betingelsen WHERE er sann. Dette kalles et sidekanalangrep. Det er lurt å begrense brukernes mulighet til å kjøre uplanlagte spørringer når de bruker sikkerhet på radnivå.
Brukstilfelle
Sikkerhet på radnivå er ideelt for mange scenarioer, inkludert:
- Når du trenger å isolere avdelingstilgang på radnivå.
- Når du trenger å begrense kundenes datatilgang til bare dataene som er relevante for firmaet.
- Når du trenger å begrense tilgangen til samsvarsformål.
Anbefalte fremgangsmåter
Her er noen anbefalte fremgangsmåter du bør vurdere når du implementerer RLS:
- Opprett et eget skjema for predikatfunksjoner og sikkerhetspolicyer.
- Unngå typekonverteringer i predikatfunksjoner.
- Unngå å bruke for mange tabellsammenføyninger og rekursjon i predikatfunksjoner.