Roles y RLS en Profundidad
El modelo de control de acceso de PostgreSQL se basa en una única idea que confunde a las personas que vienen de otras bases de datos: no existe un concepto separado de "usuario" y "grupo".
Busca en todas las páginas de la documentación
El modelo de control de acceso de PostgreSQL se basa en una única idea que confunde a las personas que vienen de otras bases de datos: no existe un concepto separado de "usuario" y "grupo".
Ambos son el mismo objeto subyacente, un rol, y todo lo demás en esta sección, desde GRANT hasta la seguridad a nivel de fila, se construye sobre esa única unificación.
GRANT) y la seguridad a nivel de fila (RLS) responden a dos preguntas diferentes: "¿puede este rol tocar esta tabla?" y "¿qué filas dentro de esa tabla puede ver?", y confundirlas causa brechas de seguridad reales.FORCE ROW LEVEL SECURITY.La documentación más antigua de PostgreSQL todavía se refiere a CREATE USER, pero ese comando es solo un envoltorio de conveniencia: crea un rol con el atributo LOGIN establecido, y nada más.
Un rol sin LOGIN se comporta como lo que otros sistemas llaman un "grupo", existiendo puramente para mantener privilegios que otros roles pueden heredar.
Ese diseño de objeto único significa que GRANT app_readers TO app_api y GRANT SELECT ON orders TO app_readers son el mismo tipo de sentencia, solo que otorgan cosas diferentes a un rol.
Una analogía útil es un sistema de insignias en un edificio: un rol es una insignia, la membresía en otro rol es enganchar una segunda insignia en tu cordón, e INHERIT decide si el lector de insignias revisa ambas insignias automáticamente o solo revisa la que presentas explícitamente.
GRANT, REVOKE y la membresía de roles juntos deciden qué objetos un rol tiene permitido tocar en absoluto: una tabla, un esquema, una función.
La seguridad a nivel de fila es una capa separada y posterior que decide qué filas dentro de un objeto un rol tiene permitido ver, una vez que ya ha superado la verificación a nivel de objeto.
Las comprobaciones de privilegios y las comprobaciones de RLS ocurren en puntos diferentes y responden a preguntas diferentes, que es la distinción más importante en todo este modelo.
Una comprobación de privilegio a nivel de objeto ocurre una vez, cuando el planificador confirma que tu rol tiene derechos SELECT, INSERT, UPDATE o DELETE sobre la tabla.
Una política de seguridad a nivel de fila, una vez habilitada, se integra en el plan de consulta como un filtro implícito, y su predicado se evalúa por fila en lugar de una vez por sentencia.
Eso significa que RLS no es un permiso en el sentido de GRANT; está más cerca de una cláusula WHERE inyectada automáticamente que el rol no puede ver ni eliminar.
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON orders
USING (tenant_id = current_setting('app.tenant_id')::int);USING controla qué filas existentes son visibles para SELECT, UPDATE y DELETE; WITH CHECK controla por separado qué filas se permite que sean una fila nueva o modificada, y omitir WITH CHECK en una política relevante para UPDATE reutiliza silenciosamente la cláusula USING en su lugar.
El propietario de la tabla y cualquier rol con el atributo BYPASSRLS omiten todas las políticas por defecto, lo cual es una elección de diseño deliberada para que los roles de administración y migración no se bloqueen accidentalmente fuera de sus propias tablas.
FORCE ROW LEVEL SECURITY existe específicamente para cerrar esa brecha cuando el rol propietario en sí mismo aún debe estar sujeto a sus propias políticas, lo que importa más cuando el código de la aplicación se ejecuta como el rol propietario.
Las políticas de RLS comúnmente se basan en una variable de sesión configurada una vez por conexión, como current_setting('app.tenant_id'), lo que reintroduce silenciosamente la distinción de estado de sesión cliente/servidor cubierta en otras partes de este manual.
Esa dependencia es exactamente por qué RLS y la agrupación de conexiones necesitan una coordinación deliberada: una conexión agrupada reutilizada entre inquilinos sin restablecer esa variable de sesión puede filtrar filas de un inquilino a la solicitud de otro inquilino.
El rendimiento es el otro lugar donde RLS se examina seriamente, porque una columna no indexada referenciada en la cláusula USING de una política convierte cada consulta contra esa tabla en un filtro implícito por fila sin un índice contra el cual podar.
Indexar las columnas en las que se filtran tus políticas, típicamente tenant_id, no es opcional a escala; es la diferencia entre que RLS no cueste nada extra y que RLS duplique silenciosamente la latencia de la consulta.
Los privilegios por defecto (ALTER DEFAULT PRIVILEGES) resuelven un problema relacionado pero distinto: sin ellos, cada tabla que crea una migración necesita que sus sentencias GRANT se repitan manualmente, y es fácil enviar una tabla nueva sin concesiones, lo que falla de forma segura para la seguridad pero rompe la aplicación en tiempo de ejecución.
| Mecanismo | Fortaleza | Debilidad | Mejor Ajuste |
|---|---|---|---|
GRANT / REVOKE (privilegios de objeto) | Simple, bien entendido, verificado una vez por sentencia | Todo o nada por objeto; no puede restringir a un subconjunto de filas | Controlar qué tablas/esquemas puede tocar un rol en absoluto |
| Políticas de seguridad a nivel de fila | Granular, aplicado incluso contra consultas ad hoc | Costo real por fila; invisible a menos que sepas que debes buscarlo | Aislamiento de filas multi-inquilino, datos con ámbito de inquilino |
Filtrado a nivel de aplicación (WHERE tenant_id = ? en código de aplicación) | Sin costo del lado de la base de datos; más simple de razonar localmente | Tan fuerte como cada ruta de código que recuerde el filtro | Herramientas internas de bajo riesgo donde un filtro omitido no es catastrófico |
CREATE USER es un envoltorio delgado alrededor de CREATE ROLE ... LOGIN; debajo, usuarios y grupos son el mismo tipo de objeto.GRANT decide si un rol puede tocar una tabla en absoluto; RLS decide qué filas de una tabla ya permitida puede ver, y las dos comprobaciones ocurren en etapas diferentes.FORCE ROW LEVEL SECURITY somete al propietario a sus propias políticas.WITH CHECK explícito, UPDATE e INSERT reutilizan la expresión USING, que a menudo no es la restricción que realmente pretendías para las escrituras.No hay ninguna a nivel de objeto: CREATE USER simplemente crea un rol con el atributo LOGIN establecido, por lo que "usuario" y "grupo" son instancias del mismo objeto ROLE.
La membresía (GRANT roleA TO roleB) hace que roleB sea miembro de roleA, y INHERIT (el valor por defecto) significa que roleB obtiene automáticamente los privilegios de roleA sin necesidad de SET ROLE primero.
GRANT controla si un rol puede tocar un objeto en absoluto (una tabla, esquema o función); RLS controla qué filas específicas dentro de una tabla ya permitida puede ver o modificar el rol.
No: los privilegios de objeto se comprueban una vez por sentencia durante la planificación, mientras que el predicado de una política de RLS se integra en el plan y se evalúa por fila.
No por defecto: los propietarios y los roles con BYPASSRLS omiten todas las políticas a menos que la tabla tenga FORCE ROW LEVEL SECURITY establecido.
USING filtra qué filas existentes son visibles para lecturas, actualizaciones y eliminaciones; WITH CHECK gobierna por separado qué filas se permite que resulten de una escritura, y por defecto utiliza la expresión USING si se omite.
Porque la política necesita algún valor por conexión para compararlo con cada fila, y una GUC de sesión configurada una vez en el momento de la conexión es la forma estándar de pasar ese contexto a la expresión de la política.
Sí, si una conexión agrupada se reutiliza entre inquilinos sin restablecer la variable de sesión de la que depende la política, lo que puede filtrar filas de un inquilino a la consulta de otro inquilino.
Puede hacerlo, especialmente si la columna referenciada en la política no está indexada, ya que el predicado se ejecuta como un filtro no indexado por fila en cada consulta contra esa tabla.
Sin ellos, cada tabla nueva que crea una migración comienza sin concesiones, por lo que un rol de aplicación puede fallar en tiempo de ejecución en una tabla que se creó correctamente pero nunca se concedió explícitamente.
Para herramientas internas de bajo riesgo donde omitir WHERE tenant_id = ? no es catastrófico; en cualquier lugar donde un filtro omitido sea un incidente de seguridad real, la garantía aplicada por el servidor de RLS vale su costo.
Solo combinado con un manejo correcto de variables de sesión e indexación: RLS aplica la lógica de la política correctamente, pero una variable de sesión filtrada o no establecida, o un predicado no indexado, socava tanto su seguridad como su rendimiento.
USING/WITH CHECK en detalleVersiones de Stack: Esta página es conceptual y no está vinculada a una versión específica de stack; las mecánicas de roles y seguridad a nivel de fila descritas aquí son consistentes en las versiones principales actuales de PostgreSQL, incluida PostgreSQL 18.4 (línea estable 18, línea de mantenimiento 17).
Revisado por Chris St. John·Última actualización: 15 jul 2026