Implementace zabezpečení na úrovni řádků
Zabezpečení na úrovni řádků (RLS) nepoužívá šifrování a funguje na úrovni databáze k omezení přístupu k tabulce pomocí zásad zabezpečení na základě členství ve skupině nebo kontextu autorizace. Tato funkce je funkčně ekvivalentní klauzuli WHERE .
Zásada zabezpečení vyvolá vloženou funkci s hodnotou tabulky, která chrání přístup k řádkům v tabulce.
V závislosti na atributu uživatele predikát určuje, jestli má daný uživatel přístup k příslušným informacím. Když spustíte dotaz na tabulku, zásada zabezpečení použije funkci predikátu. V závislosti na obchodních požadavcích může být zabezpečení na úrovni řádků stejně jednoduché WHERE CustomerId = 29 nebo složité jako povinné.
Zabezpečení na úrovni řádků podporuje dva typy zásad zabezpečení:
Predikáty filtru – omezení přístupu k datům, které porušují predikát.
Access Definice SELECT Nelze zobrazit řádky filtrované. UPDATE Nelze aktualizovat řádky filtrované. DELETE Řádky, které jsou filtrované, nelze odstranit. INSERT Nevztahuje se. Blokování predikátů – omezení změn dat, které porušují predikát.
Access Definice PO VLOŽENÍ Zabrání uživatelům v vkládání řádků s hodnotami, které porušují predikát. PO AKTUALIZACI Zabrání uživatelům v aktualizaci řádků na hodnoty, které porušují predikát. PŘED AKTUALIZACÍ Zabrání uživatelům v aktualizaci řádků, které aktuálně porušují predikát. PŘED ODSTRANĚNÍM Blokuje operace odstranění, pokud řádek porušuje predikát.
Vzhledem k tomu, že je řízení přístupu nakonfigurované a použité na úrovni databáze, jsou změny aplikace minimální – pokud existují. Uživatelé také můžou mít přímý přístup k tabulkám a dotazovat se na vlastní data.
Zabezpečení na úrovni řádků se implementuje ve třech hlavních krocích:
- Vytvořte uživatele nebo skupiny, které chcete izolovat přístup.
- Vytvořte vloženou funkci s hodnotou tabulky, která filtruje výsledky na základě definovaného predikátu.
- Vytvořte zásadu zabezpečení pro tabulku a přiřaďte dříve vytvořenou funkci.
Následující příkazy T-SQL ukazují, jak používat RLS (zabezpečení na úrovni řádků) ve scénáři, kde je přístup uživatelů oddělen podle nájemců:
-- 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;
Dále vytvořte uživatele a udělte jim přístup k tabulce Sales (Prodej ). V tomto příkladu zodpovídá každý uživatel za konkrétního tenanta. Uživatel TenantAdmin má přístup k zobrazení dat ze všech tenantů.
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
Dále vytvoříme nové schéma, vloženou funkci s hodnotou tabulky a udělíme uživateli přístup k nové funkci. Predikát WHERE @TenantName = USER_NAME() OR USER_NAME() = 'TenantAdmin' vyhodnotí, jestli uživatelské jméno, které spouští dotaz, odpovídá hodnotám sloupce Název tenanta .
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
V tuto chvíli jsme připraveni otestovat přístup:
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;
Uživatel TenantAdmin by měl vidět všechny řádky. Uživatelé Tenant1, Tenant2, Tenant3 a Tenant4 by měli vidět jenom svoje vlastní řádky.
Pokud změníte zásady zabezpečení pomocí WITH (STATE = OFF);, všimnete si, že se uživatelům zobrazí všechny řádky.
Poznámka:
Existuje riziko úniku informací, pokud útočník zapíše dotaz se speciálně vytvořenou WHERE klauzulí a například chybou dělení nulou, aby vynutil výjimku, pokud WHERE je podmínka pravdivá. To se označuje jako útok na straně kanálu. Je vhodné omezit možnost uživatelů spouštět neplánované dotazy při používání zabezpečení na úrovni řádků.
Případ použití
Zabezpečení na úrovni řádků je ideální pro mnoho scénářů, mezi které patří:
- Pokud potřebujete izolovat přístup oddělení na úrovni řádku.
- Pokud potřebujete omezit přístup zákazníků k datům jenom na data relevantní pro jejich společnost.
- Pokud potřebujete omezit přístup pro účely dodržování předpisů.
Osvědčené postupy
Tady je několik osvědčených postupů, které byste měli zvážit při implementaci zabezpečení na úrovni řádků:
- Vytvořte samostatné schéma pro predikátové funkce a zásady zabezpečení.
- Vyhněte se převodům typů ve funkcích predikátu.
- V predikátech funkcí nepoužívejte nadměrné spojení tabulek a rekurzi.