Cómo implementar Row-Level Security en SQL Server
Row-Level Security (RLS) en SQL Server permite que cada usuario vea solo las filas de una tabla que le corresponden, sin duplicar tablas ni cambiar el código de las aplicaciones. Es una pieza central de la gobernanza de datos, porque la regla de acceso queda guardada en la propia base de datos y se aplica automáticamente a cualquier consulta — venga de un informe en Power BI, de una API o de SQL Server Management Studio. En este paso a paso vas a crear una política de RLS desde cero y comprobarla con usuarios diferentes.
Requisitos previos
- SQL Server 2016 o superior, o una base de datos en Azure SQL Database.
- Una herramienta de consulta, como SQL Server Management Studio (SSMS) o Azure Data Studio.
- Permisos para crear tablas, funciones y políticas de seguridad (por ejemplo, el rol db_owner en una base de datos de pruebas).
- Conocimientos básicos de T-SQL, como CREATE TABLE y SELECT.
La idea detrás de RLS es simple y se apoya en dos piezas: una función de predicado, que decide si una fila es visible, y una política de seguridad, que aplica esa función a una tabla. Vamos a construir las dos.
Paso 1: Crear una tabla de ejemplo
Empezamos con una tabla de ventas donde cada fila pertenece a un comercial. La columna SalesRep guarda el nombre del usuario dueño de la fila; es esa columna la que la política usará para filtrar.
CREATE TABLE dbo.Sales
(
SaleId INT IDENTITY PRIMARY KEY,
SalesRep SYSNAME,
Product NVARCHAR(50),
Amount DECIMAL(10,2)
);
INSERT INTO dbo.Sales (SalesRep, Product, Amount) VALUES
('ana', 'Teclado', 120.00),
('bruno', 'Monitor', 240.00),
('ana', 'Rato', 45.00);
Paso 2: Crear usuarios para probar
Para ver RLS en acción necesitamos usuarios diferentes. Creamos dos usuarios sin login — bastan para la demostración — y les damos permiso de lectura sobre la tabla.
CREATE USER ana WITHOUT LOGIN;
CREATE USER bruno WITHOUT LOGIN;
GRANT SELECT ON dbo.Sales TO ana, bruno;
Paso 3: Crear la función de predicado
La regla de acceso vive en una función en línea con valor de tabla (inline table-valued function). Devuelve el valor 1 cuando la fila debe aparecer para el usuario. En este ejemplo, una fila es visible si el valor de SalesRep es igual al nombre del usuario actual, obtenido con USER_NAME(). Guardamos la función en un schema propio, llamado Security, por organización y buenas prácticas.
CREATE SCHEMA Security;
GO
CREATE FUNCTION Security.fn_vendasPorComercial(@SalesRep AS SYSNAME)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN SELECT 1 AS resultado
WHERE @SalesRep = USER_NAME() OR USER_NAME() = 'dbo';
GO
La cláusula WITH SCHEMABINDING es obligatoria para funciones usadas en RLS. La condición extra USER_NAME() = 'dbo' garantiza que el administrador sigue viendo todas las filas, algo práctico para tareas de gestión y mantenimiento.
Paso 4: Crear la política de seguridad
Solo falta conectar la función a la tabla. Una SECURITY POLICY con un FILTER PREDICATE aplica la función a la columna SalesRep de la tabla dbo.Sales. Con STATE = ON, la política queda activa de inmediato.
CREATE SECURITY POLICY Security.VendasFiltro
ADD FILTER PREDICATE Security.fn_vendasPorComercial(SalesRep)
ON dbo.Sales
WITH (STATE = ON);
Un FILTER PREDICATE oculta las filas en las lecturas (SELECT, UPDATE, DELETE). También existe el BLOCK PREDICATE, que impide que un usuario inserte o cambie filas fuera de su ámbito — útil cuando también quieres proteger la escritura.
Comprobar el resultado
Ahora podemos consultar la tabla fingiendo ser cada usuario, con EXECUTE AS. La comercial "ana" debe ver solo sus dos filas y "bruno" solo la suya.
EXECUTE AS USER = 'ana';
SELECT * FROM dbo.Sales; -- devolve as 2 linhas da ana
REVERT;
EXECUTE AS USER = 'bruno';
SELECT * FROM dbo.Sales; -- devolve a 1 linha do bruno
REVERT;
Si cada usuario ve solo sus propias filas, RLS está funcionando. Fíjate en que la consulta es exactamente la misma en ambos casos: el filtrado ocurre de forma transparente, sin que nadie tenga que añadir un WHERE. Esa transparencia es lo que hace a RLS tan útil — la regla se queda en el lugar correcto, la base de datos, en vez de repartida por decenas de informes.
Conclusión
Con una función de predicado y una política de seguridad ya controlas el acceso a los datos fila a fila, directamente en SQL Server. A partir de aquí puedes hacer la regla más dinámica — por ejemplo, usar SESSION_CONTEXT para identificar al usuario de la aplicación en escenarios multi-tenant — o reforzar la escritura con un BLOCK PREDICATE. Para desactivar la política durante una prueba, basta con ALTER SECURITY POLICY Security.VendasFiltro WITH (STATE = OFF). ¿Qué otra tabla de tu modelo se beneficiaría de una regla de acceso a nivel de fila?