Varför är säkerhet på radnivå viktig?
Före SQL Server 2016 var säkerhet på tabellnivå standardens lägsta säkerhetsnivå för en databas. Med andra ord kan en användare begränsas till att få tillgång till en tabell som helhet. I vissa fall behöver vi dock användare ha tillgång till en tabell, men inte till specifika rader i tabellen. Före SQL Server 2016 krävde detta att anpassade lagrade procedurer skrevs för tillhandahållande av sådan finkornig säkerhet. Sådana lagrade procedurer är dock utsatta för SQL-injektion och andra säkerhetsförbehåll.
Använda SQL Server Row Level-säkerhetsfunktionen på praktiken
SQL Server 2016 introducerade en ny säkerhetsfunktion på radnivå som tillåter användare att ha tillgång till en tabell men begränsar dem till åtkomst till specifika rader i den tabellen. Låt oss ta en titt på hur detta kan användas praktiskt.
Beskrivning
Det finns fyra steg för att implementera säkerhet på radnivå i SQL Server.
- Ge Select-behörigheter till användarna i tabellen där du vill implementera säkerhet på radnivå.
- Närnäst måste du skriva en inline-tabellvärdefunktion som innehåller ett filterpredikat. Lägg till filterlogiken till filterpredikatet.
- Slutligen måste du binda filterpredikatet som du skapade i det andra steget till en säkerhetspolicy.
- Testa säkerhetsfunktionen på radnivå.
Innan vi utför stegen ovan måste vi skapa en dummydatabas med några dummyposter. Kör följande skript för att göra det:
SKAPA DATABAS UniversityGOUSE UniversityGOUSE UniversityCREATE TABELL Personer(Id INT PRIMÄR NYCKELIDENTITET(1,1),Namn VARCHAR (50),Roll VARCHAR (50))GOUSE UniversityINSERT INTO Persons VALUES ('Sally', 'Prektor' )INSERT INTO Persons VALUES ('Edward', 'Student' )INSERT INTO Persons VALUES ('Jon', 'Student' )INSERT INTO Persons VALUES ('Scot', 'Student')INSERT INTO Persons VALUES ('Ben', 'Student') INSERT INTO Persons VALUES ('Isabel', 'Lärare' )INSERT INTO Persons VALUES ('David', 'Lärare' )INSERT INTO Persons VALUES ('Laura', 'Lärare' )INSERT INTO Persons VALUES ('Jean', 'Lärare' ')INSERT INTO Persons VALUES ('Francis', 'Lärare')
I manuset skapar vi en dummydatabas "Universitet". Därefter kör vi skriptet som skapar en tabell med namnet "Personer". Om du tittar på tabelldesignen kan du se att den innehåller tre kolumner Id, Name och Roll. Id-kolumnen är den primära nyckelkolumnen med IDENTITY-begränsning. Kolumnen Namn innehåller namnet på personen och kolumnen Roll innehåller personens roll. Slutligen infogade vi 10 poster i tabellen Personer. Tabellen har 1 rektor, 4 lärare och 5 elever.
Låt oss köra en enkel SELECT-sats för att se poster i tabellen:
Använd UniversitySELECT * FROM Persons
Resultatet ser ut så här:
Vi vill att användaren som heter Principal ska ha tillgång till alla rader i tabellen Personer. På samma sätt bör en lärare endast ha tillgång till lärarposterna, medan studenter endast ska ha tillgång till studentposter. Detta är ett klassiskt fall av säkerhet på radnivå.
För att implementera säkerheten på radnivån kommer vi att följa stegen som vi diskuterade tidigare.
Steg 1:Ge utvalda behörigheter till användare på bordet
Låt oss skapa tre användare med rollerna Rektor, Lärare och Student och ge dem SELECT-åtkomst till dessa användare i tabellen Personer. Kör följande skript för att göra det:
SKAPA ANVÄNDARE Rektor UTAN LOGGA IN;GOGRANT VÄLJ PÅ Personer TILL Lärare;GOGRANT SELECT ON Personer TILL Student;GOGRANT SELECT ON Personer TILL Student;GO
Steg 2:Skapa filterpredikat
När användarna har beviljats behörigheter är nästa steg att skapa ett filterpredikat.
Följande skript gör det:
Använd UniversityGOCREATE FUNCTION dbo.fn_SP_Person(@Role AS sysname) RETURNER TABLEWITH SCHEMABINDINGAS RETURN SELECT 1 AS fn_SP_Person_output -- Predikatlogik WHERE @Role =USER_NAME() ELLER USER_NAME>()';Principal()';Filterpredikatet skapas i en inline-tabellvärderad funktion och tar rollen som användaren som en parameter. Den returnerar de poster där rollvärdet som skickats som en parameter matchar rollvärdet i kolumnen Roll. Eller om användarrollen är 'Principal', returneras alla roller. Om du tittar på predikatfiltret hittar du inte tabellnamnet som vi skapar filtret för. Filterpredikatet är kopplat till tabellen via säkerhetspolicyn som vi kommer att se i nästa steg.
Steg 3:Skapa en säkerhetspolicy
Utför följande skript för att skapa en säkerhetspolicy för filterpredikatet som vi skapade i det sista steget:
Använd UniversityGoCREATE SÄKERHETSPOLICY RoleFilterADD FILTER PREDICATE dbo.fn_SP_Person(Role)ON dbo.PersonsWITH (STATE =ON);GOI säkerhetspolicyn har vi helt enkelt lagt till filterpredikatet vi skapade i tabellen Personer. För att aktivera policyn ska flaggan "STATE" vara inställd på ON.
Steg 4:Testa säkerhet på radnivå
Vi utförde alla steg som behövdes för att upprätthålla säkerhet på radnivå i tabellen Personer i universitetsdatabasen. Låt oss först försöka komma åt posterna i tabellen Personer via standardanvändare. Kör följande skript:
Använd UniversitySELECT * FROM Persons;GODu kommer inte att se något i utdata eftersom standardanvändaren inte kan komma åt tabellen Personer.
Låt oss byta till studentanvändaren som vi skapade tidigare och försöka VÄLJA poster från tabellen Personer:
EXECUTE AS USER ='Student';Använd UniversitySELECT * FROM Persons; -- Endast studentrekordREVERT;GOI skriptet ovan byter vi till "Student"-användaren, valde poster från tabellen Personer och återgår till standardanvändaren. Utdatan ser ut så här:
Du kan se att på grund av säkerheten på radnivå visas endast poster där kolumnen Roll har värdet Student.
På samma sätt har användaren Lärare endast åtkomst till poster där Rollkolumnen har värdet Lärare. Kör följande skript för att verifiera detta:
EXECUTE AS USER ='Lärare';Använd UniversitySELECT * FROM Persons; -- Alla posterREVERT;GOI utgången kommer du att registrera följande:
Slutligen, i vårt filterpredikat, implementerade vi logiken att användaren Principal kan komma åt alla poster. Låt oss verifiera detta genom att utföra följande fråga:
EXECUTE AS USER ='Rektor';Använd UniversitySELECT * FROM Persons; -- Alla posterREVERT;GOI utgången kommer du att se alla poster enligt nedan:
Slutsats
Säkerhetsfunktionen på radnivå är extremt användbar när du vill att användare ska ha finkornig tillgång till specifik data. Säkerhetsfunktionen på radnivå involverar dock inline-tabellvärderad funktion, vilket kan leda till att du drabbas av prestanda.
Som en tumregel, om du planerar att använda en enkel WHERE-sats i predikatfunktionen, bör din prestation inte påverkas. Å andra sidan bör komplexa join-satser som involverar uppslagstabeller undvikas när du har implementerat säkerhet på radnivå.