RLS y CLS en Microsoft Fabric: Seguridad de datos en Warehouse y Lakehouse
En proyectos de Business Intelligence de escala empresarial, la seguridad no puede ser una capa opcional que se añade al final del desarrollo. Tradicionalmente, muchos consultores han gestionado la seguridad directamente en Power BI. Sin embargo, con la llegada de Microsoft Fabric, el paradigma cambia. Centralizar la seguridad en la capa de datos (Warehouse o SQL Analytics Endpoint del Lakehouse) permite que cualquier herramienta que consuma esos datos —ya sea un informe de Power BI, un notebook de Python o una consulta directa por SQL— respete las mismas restricciones.
Implementar Row-Level Security (RLS) y Column-Level Security (CLS) mediante T-SQL es una decisión de arquitectura estratégica. Resuelve el problema de la redundancia: no necesitas replicar reglas de seguridad en cada archivo .pbix. Además, reduce el riesgo de fugas de datos cuando los usuarios avanzados conectan con herramientas externas. No obstante, esta centralización tiene un coste en complejidad técnica y, si no se diseña correctamente, en el rendimiento de las consultas.
En este artículo, abordaremos cómo configurar estas capas de seguridad de forma dinámica. Veremos cómo pasar de un modelo de seguridad estática a uno basado en tablas de mapeo que escalan con la organización, minimizando el mantenimiento manual.
Diferencia entre la seguridad de área de trabajo y la seguridad de datos
Es un error común confundir los roles del área de trabajo (Workspace) con la seguridad granular del motor SQL. Si un usuario tiene el rol de ‘Member’ o ‘Admin’ en el Workspace de Fabric, ignorará cualquier restricción de RLS o CLS que hayamos configurado en el Warehouse. Para que la seguridad a nivel de fila y columna sea efectiva, el usuario debe acceder al Warehouse o Lakehouse con el rol de ‘Viewer’ o mediante permisos compartidos específicos (Read).
Para entender mejor dónde encaja cada pieza en la arquitectura, es recomendable revisar la Lakehouse vs Data Warehouse en Microsoft Fabric: Guía de Arquitectura y Selección. En ambos casos, el punto de entrada para la seguridad granular es el SQL Analytics Endpoint.
Implementación de Row-Level Security (RLS) dinámico
El RLS dinámico en el Warehouse de Fabric se basa en tres componentes: una tabla de mapeo de seguridad, una función de predicado (Inline Table-Valued Function) y una política de seguridad (Security Policy). El objetivo es que la función identifique quién está ejecutando la consulta y filtre los registros de la tabla de hechos basándose en su identidad.
Paso 1: Tabla de mapeo de seguridad
En lugar de codificar nombres de usuario en la función, utilizamos una tabla que relacione el UPN (User Principal Name) con el atributo de filtrado, por ejemplo, el ID de la Delegación o el Centro de Coste.
-- Creamos la tabla de seguridad en un esquema dedicado
CREATE SCHEMA [Security];
GO
CREATE TABLE [Security].[UserPermissions] (
[UserEmail] NVARCHAR(256),
[DelegacionID] INT
);
GO
-- Insertamos algunos permisos de prueba
INSERT INTO [Security].[UserPermissions] ([UserEmail], [DelegacionID])
VALUES ('consultor1@zondeal.com', 10), ('director@zondeal.com', 20);Paso 2: Creación de la Función de Predicado
Esta función debe ser altamente eficiente. El motor de Fabric la ejecutará para cada fila (lógicamente), por lo que debemos evitar transformaciones complejas dentro de ella. La función utiliza USER_NAME() para capturar la identidad del usuario actual.
CREATE FUNCTION [Security].[fn_SecurityPredicate](@DelegacionID AS INT)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN SELECT 1 AS Result
WHERE
-- El usuario es administrador (opcional)
IS_MEMBER('AAD_Admins') = 1
OR
-- El usuario tiene permiso explícito en la tabla de mapeo
EXISTS (
SELECT 1 FROM [Security].[UserPermissions]
WHERE [UserEmail] = USER_NAME()
AND [DelegacionID] = @DelegacionID
);Paso 3: Aplicación de la Security Policy
Finalmente, vinculamos la función a la tabla de hechos. A partir de este momento, cualquier consulta sobre FactVentas pasará por el filtro de la función de predicado.
CREATE SECURITY POLICY [Security].[SalesFilter]
ADD FILTER PREDICATE [Security].[fn_SecurityPredicate]([DelegacionID])
ON [dbo].[FactVentas]
WITH (STATE = ON);Column-Level Security (CLS): Protegiendo datos sensibles
A diferencia de RLS, la seguridad por columnas no requiere funciones de predicado. Se gestiona directamente con el comando GRANT SELECT a nivel de columna. Es ideal para ocultar márgenes de beneficio, datos personales (PII) o costes de adquisición a perfiles que solo necesitan ver volúmenes de ventas.
Si un usuario intenta realizar un SELECT * sobre una tabla donde tiene restricciones de columna, la consulta fallará. Esto obliga a los analistas a ser explícitos en sus consultas, seleccionando solo las columnas para las que tienen permiso.
-- Denegar acceso a la columna ImporteCosto en la tabla de Ventas
-- Primero nos aseguramos de que el usuario tiene acceso general
GRANT SELECT ON [dbo].[FactVentas] TO [analista_junior@zondeal.com];
-- Revocamos el acceso a la columna específica
DENY SELECT ON [dbo].[FactVentas]([ImporteCosto]) TO [analista_junior@zondeal.com];Es importante destacar que el mantenimiento de CLS puede volverse tedioso si se hace usuario por usuario. La mejor práctica es utilizar Grupos de Seguridad de Entra ID (antes Azure AD) y aplicar los permisos al grupo.
Comparativa de enfoques de seguridad
| Característica | RLS en Power BI | RLS/CLS en Fabric Warehouse |
|---|---|---|
| Centralización | Baja (por cada modelo semántico) | Alta (en la fuente de datos) |
| Herramientas | Solo informes Power BI | Power BI, SQL, Python, Excel |
| Complejidad | Media (DAX) | Alta (T-SQL y Permisos SQL) |
| Rendimiento | Optimizado para Vertipaq | Depende de la optimización del motor SQL |
| Seguridad por columnas | Limitada (vía OLS) | Nativa mediante GRANT/DENY |
Consideraciones de rendimiento y optimización
Aplicar RLS en el Warehouse introduce un paso adicional en el plan de ejecución de cada consulta. Si la tabla de mapeo es muy grande o si la función de predicado realiza joins costosos, el rendimiento se degradará notablemente. En proyectos reales, he visto consultas que pasan de 2 segundos a 20 segundos tras una implementación deficiente de RLS.
Para mitigar esto, es fundamental que la columna utilizada para filtrar (en nuestro ejemplo DelegacionID) esté correctamente distribuida y que la tabla de mapeo sea pequeña. Si trabajas con volúmenes masivos, consulta la Optimización del motor de consultas en Fabric Warehouse: Guía de rendimiento avanzado para entender cómo el motor de Fabric gestiona los predicados.
Otro aspecto crítico es la fragmentación. Aunque Fabric gestiona gran parte del mantenimiento, una lógica de seguridad compleja puede exacerbar los problemas de escaneo de datos. Puedes profundizar en esto en la Guía de Compactación Automática y Optimización de Consultas.
Errores frecuentes en la implementación
- Uso de funciones no deterministas: Incluir lógica que cambie en cada ejecución dentro del predicado de seguridad impide que el motor cachee los planes de consulta.
- No validar con ‘EXECUTE AS USER’: Muchos desarrolladores asumen que el RLS funciona sin probarlo con diferentes perfiles. Usa siempre
EXECUTE AS USER = 'email@dominio.com'para validar los resultados antes de pasar a producción. - Circularidad en permisos: Intentar securizar la tabla de mapeo con la misma función que lee de ella provocará errores de recursividad o bloqueos.
- Ignorar el impacto en el DirectLake: Cuando usamos RLS en el Warehouse, el modo DirectLake de Power BI puede hacer un fallback a DirectQuery si la seguridad no está configurada para ser compatible, lo que reduce drásticamente el rendimiento visual.
Para aquellos que vienen del mundo tradicional de Power BI, es vital comprender que el RLS dinámico en SQL se comporta de forma distinta al DAX. Mientras que en DAX el filtro se propaga por las relaciones del modelo, en SQL el filtro debe aplicarse explícitamente a cada tabla que queramos securizar. Si quieres comparar ambos mundos, te recomiendo leer sobre RLS en Power BI: seguridad por filas estática, dinámica y validación en el servicio.
Checklist para una seguridad robusta en Fabric
- Centralizar usuarios: Utiliza siempre grupos de seguridad de Entra ID en lugar de usuarios individuales.
- Esquema de seguridad: Crea un esquema separado (ej.
[Security]) para todas las funciones y tablas de mapeo; no mezcles lógica de seguridad con tablas de negocio. - Validación de identidad: Decide entre
USER_NAME()ySUSER_SNAME()según cómo se conecten tus aplicaciones. - Documentación: Registra qué tablas tienen políticas activas. Es frustrante para un analista no entender por qué no ve datos si no sabe que existe un RLS subyacente.
- Monitoreo: Revisa las métricas de capacidad para identificar picos de CPU causados por funciones de predicado ineficientes.
Preguntas frecuentes
¿Puedo aplicar RLS en un Lakehouse de Fabric?
Sí, pero se aplica a través del SQL Analytics Endpoint. Las restricciones de RLS/CLS configuradas por T-SQL solo afectarán a las consultas que pasen por dicho endpoint SQL. Si un usuario accede a los archivos Parquet directamente a través de Spark o mediante Data Access Roles en Fabric, las reglas de SQL no se aplicarán.
¿Qué ocurre si un usuario necesita ver datos de dos delegaciones diferentes?
La tabla de mapeo debe soportar relaciones de uno a muchos. En nuestra tabla [Security].[UserPermissions], simplemente insertarías dos filas para el mismo usuario con diferentes IDs de delegación. La función EXISTS en el predicado gestionará esto correctamente devolviendo datos para ambos IDs.
¿Afecta el RLS al rendimiento de los informes en Direct Lake?
Sí. Actualmente, si se define RLS en el motor SQL de Fabric, el modelo semántico de Power BI puede verse obligado a salir del modo Direct Lake y entrar en Direct Query para garantizar que la seguridad se evalúa en tiempo real en la fuente. Esto es algo que debe medirse con el Performance Analyzer de Power BI.
¿Cómo puedo deshabilitar temporalmente la seguridad para depuración?
No necesitas borrar la función. Puedes simplemente desactivar la política con el comando ALTER SECURITY POLICY [Security].[SalesFilter] WITH (STATE = OFF);. Recuerda volver a activarla inmediatamente tras las pruebas.





