Implementación de la seguridad de nivel de fila

Completado

La seguridad de nivel de fila (RLS) no usa cifrado y se aplica en la base de datos para restringir el acceso a una tabla mediante una directiva de seguridad basada en el contexto de autorización o pertenencia a grupos. Es funcionalmente equivalente a una cláusula WHERE.

La directiva de seguridad invoca una función insertada con valores de tabla para proteger el acceso a las filas de una tabla.

Según el atributo de un usuario, el predicado determina si ese usuario tiene acceso a la información pertinente. Al ejecutar una consulta en una tabla, la directiva de seguridad aplica la función de predicado. Según los requisitos empresariales, RLS puede ser tan simple como WHERE CustomerId = 29 o tan compleja como sea necesario.

Hay dos tipos de directivas de seguridad compatibles con la seguridad de nivel de fila:

  • Predicados de filtro: restringen el acceso a datos que infringe el predicado.

    Access Definición
    SELECT No se pueden ver las filas filtradas.
    UPDATE No se pueden actualizar las filas filtradas.
    DELETE No se pueden eliminar las filas filtradas.
    INSERT No aplicable.
  • Predicados de bloqueo: restringen los cambios de datos que infringen el predicado.

    Access Definición
    DESPUÉS DE INSERTAR Impide que los usuarios inserten filas con valores que infrinjan el predicado.
    AFTER UPDATE Impide que los usuarios actualicen filas a valores que infrinjan el predicado.
    ANTES DE ACTUALIZAR Impide que los usuarios actualicen filas que infrinjan el predicado en la actualidad.
    ANTES DE ELIMINAR Bloquea las operaciones de eliminación si la fila infringe el predicado.

Dado que el control de acceso está configurado y se aplica en el nivel de base de datos, los cambios en la aplicación son mínimos, si es que los hay. Además, los usuarios pueden tener acceso directamente a las tablas y pueden consultar sus propios datos.

La seguridad de nivel de fila se implementa en tres pasos principales:

  1. Cree los usuarios o grupos a los que desea aislar el acceso.
  2. Cree la función insertada con valores de tabla que filtrará los resultados en función del predicado definido.
  3. Cree una directiva de seguridad para la tabla y asigne la función creada anteriormente.

Los comandos de T-SQL siguientes muestran cómo usar RLS en un escenario en el que el inquilino segrega el acceso de usuario:

-- 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;

A continuación, cree los usuarios y concédales acceso a la tabla Ventas. En este ejemplo, cada usuario es responsable de un inquilino específico. El usuario TenantAdmin tiene acceso para ver los datos de todos los inquilinos.

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

A continuación, crearemos un nuevo esquema, una función insertada con valores de tabla y concederemos al usuario acceso a la nueva función. El predicado WHERE @TenantName = USER_NAME() OR USER_NAME() = 'TenantAdmin' evalúa si el nombre de usuario que ejecuta la consulta coincide con los valores de columna TenantName.

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

En este momento, estamos listos para probar el acceso:

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;

El usuario TenantAdmin debe ver todas las filas. Los usuarios Tenant1, Tenant2, Tenant3 y Tenant4 solo deben ver sus propias filas.

Si modifica la directiva de seguridad con WITH (STATE = OFF);, observará que los usuarios verán todas las filas.

Screenshot of T-SQL commands to alter a security policy.

Nota:

Existe un riesgo de pérdida de información si un atacante escribe una consulta con una cláusula WHERE especialmente diseñada y, por ejemplo, un error de dividir por cero, para forzar una excepción si se cumple la condición WHERE. Esto se conoce como ataque de canal lateral. Es aconsejable limitar la capacidad de los usuarios de ejecutar consultas ad hoc al usar la seguridad de nivel de fila.

Caso de uso

La seguridad de nivel de fila es ideal para muchos escenarios, entre los que se incluyen:

  • Cuando necesite aislar el acceso departamental en el nivel de fila.
  • Cuando necesite restringir el acceso de los clientes solo a los datos pertinentes para la empresa.
  • Cuando necesite restringir el acceso con fines de cumplimiento.

Práctica recomendada

Estos son algunos procedimientos recomendados que se deben tener en cuenta al implementar RLS:

  • Se recomienda crear un esquema independiente para funciones de predicado y directivas de seguridad.
  • Siempre que sea posible, evite las conversiones de tipos en las funciones de predicado.
  • Para maximizar el rendimiento, evite la combinación excesiva de tablas y la recursividad en las funciones de predicado.