Además del sistema de privilegios estándar SQL disponible a través de GRANT, las tablas pueden tener políticas de seguridad de fila que restringen, por usuario, qué filas pueden ser devueltas por consultas normales o insertadas, actualizadas o eliminadas por comandos de modificación de datos. Esta característica también se conoce como Seguridad a nivel de fila. De forma predeterminada, las tablas no tienen ninguna política, de modo que si un usuario tiene privilegios de acceso a una tabla de acuerdo con el sistema de privilegios SQL, todas las filas dentro de ella están igualmente disponibles para consultas o actualizaciones.

Cuando la seguridad de fila está habilitada en una tabla (con ALTER TABLE … ENABLE ROW LEVEL SECURITY), todo acceso normal a la tabla para seleccionar filas o modificar filas debe estar permitido por una política de seguridad de filas. (Sin embargo, el propietario de la tabla no suele estar sujeto a las políticas de seguridad de filas). Si no existe ninguna política para la tabla, se utiliza una política de denegación predeterminada, lo que significa que no hay filas visibles ni se pueden modificar. Operaciones que se aplican a toda la tabla, como TRUNCATE y REFERENCES, no están sujetos a la seguridad de la fila.

Las políticas de seguridad de filas pueden ser específicas de comandos, roles o ambos. Se puede especificar una política para aplicar a ALL comandos, o para SELECT, INSERT, UPDATE, o DELETE. Se pueden asignar varios roles a una política determinada y se aplican las reglas normales de herencia y pertenencia al rol.

Para especificar qué filas son visibles o modificables según una política, se requiere una expresión que devuelva un resultado booleano. Esta expresión se evaluará para cada fila antes de cualquier condición o función que provenga de la consulta del usuario. (Las únicas excepciones a esta regla son leakproof funciones, que están garantizadas para no filtrar información; el optimizador puede optar por aplicar dichas funciones antes de la comprobación de seguridad de filas). Filas para las que la expresión no devuelve true no será procesado. Se pueden especificar expresiones separadas para proporcionar un control independiente sobre las filas que son visibles y las filas que pueden modificarse. Las expresiones de política se ejecutan como parte de la consulta y con los privilegios del usuario que ejecuta la consulta, aunque las funciones de definición de seguridad se pueden utilizar para acceder a datos que no están disponibles para el usuario que llama.

Superusuarios y roles con el BYPASSRLS El atributo siempre omite el sistema de seguridad de filas al acceder a una mesa. Los propietarios de mesas normalmente también pasan por alto la seguridad de las filas, aunque el propietario de una mesa puede optar por estar sujeto a la seguridad de las filas con ALTER TABLE … FORCE ROW LEVEL SECURITY.

Habilitar y deshabilitar la seguridad de filas, así como agregar políticas a una tabla, es siempre un privilegio exclusivo del propietario de la tabla.

Las políticas se crean usando el comando CREATE POLICY, se modifican usando el comando ALTER POLICY y se eliminan usando el comando DROP POLICY. Para habilitar y deshabilitar la seguridad de filas para una tabla determinada, use el comando ALTER TABLE.

Cada política tiene un nombre y se pueden definir varias políticas para una tabla. Como las políticas son específicas de la tabla, cada política de una tabla debe tener un nombre exclusivo. Diferentes tablas pueden tener políticas con el mismo nombre.

Cuando se aplican varias políticas a una consulta determinada, se combinan mediante OR (para políticas permisivas, que son las predeterminadas) o usando AND (para políticas restrictivas). Esto es similar a la regla de que un rol determinado tiene los privilegios de todos los roles de los que es miembro. Las políticas permisivas frente a las restrictivas se analizan más adelante.

Como ejemplo simple, aquí se explica cómo crear una política en el account relación para permitir sólo miembros de la managers rol para acceder a filas, y solo filas de sus cuentas:

CREATETABLE accounts (manager text, company text, contact_email text);ALTERTABLE accounts ENABLEROWLEVEL SECURITY;CREATE POLICY account_managers ON accounts TO managers
    USING(manager =current_user);

La política anterior proporciona implícitamente una WITH CHECK cláusula idéntica a su USING cláusula, de modo que la restricción se aplique tanto a las filas seleccionadas por un comando (por lo que un administrador no puede SELECT, UPDATE, o DELETE filas existentes que pertenecen a un administrador diferente) y a filas modificadas por un comando (por lo que las filas que pertenecen a un administrador diferente no se pueden crear a través de INSERT o UPDATE).

Si no se especifica ningún rol o el nombre de usuario especial PUBLIC se utiliza, la política se aplica a todos los usuarios del sistema. Para permitir que todos los usuarios accedan solo a su propia fila en una users tabla, se puede utilizar una política simple:

CREATE POLICY user_policy ON users
    USING(user_name =current_user);

Esto funciona de manera similar al ejemplo anterior.

Para utilizar una política diferente para las filas que se agregan a la tabla en comparación con las filas que están visibles, se pueden combinar varias políticas. Este par de políticas permitiría a todos los usuarios ver todas las filas en el users tabla, pero solo modificar la suya propia:

CREATE POLICY user_sel_policy ON users
    FORSELECTUSING(true);CREATE POLICY user_mod_policy ON users
    USING(user_name =current_user);

en un SELECT comando, estas dos políticas se combinan usando OR, con el efecto neto de que se pueden seleccionar todas las filas. En otros tipos de comando, solo se aplica la segunda política, por lo que los efectos son los mismos que antes.

La seguridad de la fila también se puede desactivar con el ALTER TABLE mando. La desactivación de la seguridad de filas no elimina ninguna política definida en la tabla; simplemente se ignoran. Entonces, todas las filas de la tabla son visibles y modificables, sujeto al sistema estándar de privilegios SQL.

A continuación se muestra un ejemplo más amplio de cómo se puede utilizar esta función en entornos de producción. La mesa passwd emula un archivo de contraseña de Unix:

-- Simple passwd-file based exampleCREATETABLE passwd (
  user_name             textUNIQUENOTNULL,
  pwhash                text,
  uid                   intPRIMARYKEY,
  gid                   intNOTNULL,
  real_name             textNOTNULL,
  home_phone            text,
  extra_info            text,
  home_dir              textNOTNULL,
  shell                 textNOTNULL);CREATE ROLE admin;-- AdministratorCREATE ROLE bob;-- Normal userCREATE ROLE alice;-- Normal user-- Populate the tableINSERTINTO passwd VALUES('admin','xxx',0,0,'Admin','111-222-3333',null,'/root','/bin/dash');INSERTINTO passwd VALUES('bob','xxx',1,1,'Bob','123-456-7890',null,'/home/bob','/bin/zsh');INSERTINTO passwd VALUES('alice','xxx',2,1,'Alice','098-765-4321',null,'/home/alice','/bin/zsh');-- Be sure to enable row level security on the tableALTERTABLE passwd ENABLEROWLEVEL SECURITY;-- Create policies-- Administrator can see all rows and add any rowsCREATE POLICY admin_all ON passwd TO admin USING(true)WITHCHECK(true);-- Normal users can view all rowsCREATE POLICY all_view ON passwd FORSELECTUSING(true);-- Normal users can update their own records, but-- limit which shells a normal user is allowed to setCREATE POLICY user_mod ON passwd FORUPDATEUSING(current_user= user_name)WITHCHECK(current_user= user_name AND
    shell IN('/bin/bash','/bin/sh','/bin/dash','/bin/zsh','/bin/tcsh'));-- Allow admin all normal rightsGRANTSELECT,INSERT,UPDATE,DELETEON passwd TO admin;-- Users only get select access on public columnsGRANTSELECT(user_name, uid, gid, real_name, home_phone, extra_info, home_dir, shell)ON passwd TOpublic;-- Allow users to update certain columnsGRANTUPDATE(pwhash, real_name, home_phone, extra_info, shell)ON passwd TOpublic;

Al igual que con cualquier configuración de seguridad, es importante probar y asegurarse de que el sistema se comporte como se espera. Usando el ejemplo anterior, esto demuestra que el sistema de permisos está funcionando correctamente.

-- admin can view all rows and fields
postgres=>set role admin;SET
postgres=>table passwd;
 user_name | pwhash | uid | gid | real_name |  home_phone  | extra_info | home_dir    |   shell
-----------+--------+-----+-----+-----------+--------------+------------+-------------+-----------
 admin     | xxx    |0|0| Admin     |111-222-3333||/root       |/bin/dash
 bob       | xxx    |1|1| Bob       |123-456-7890||/home/bob   |/bin/zsh
 alice     | xxx    |2|1| Alice     |098-765-4321||/home/alice |/bin/zsh
(3rows)-- Test what Alice is able to do
postgres=>set role alice;SET
postgres=>table passwd;
ERROR:  permission denied for relation passwd
postgres=>select user_name,real_name,home_phone,extra_info,home_dir,shell from passwd;
 user_name | real_name |  home_phone  | extra_info | home_dir    |   shell
-----------+-----------+--------------+------------+-------------+-----------
 admin     | Admin     |111-222-3333||/root       |/bin/dash
 bob       | Bob       |123-456-7890||/home/bob   |/bin/zsh
 alice     | Alice     |098-765-4321||/home/alice |/bin/zsh
(3rows)

postgres=>update passwd set user_name ='joe';
ERROR:  permission denied for relation passwd
-- Alice is allowed to change her own real_name, but no others
postgres=>update passwd set real_name ='Alice Doe';UPDATE1
postgres=>update passwd set real_name ='John Doe'where user_name ='admin';UPDATE0
postgres=>update passwd set shell ='/bin/xx';
ERROR:  new row violates WITHCHECKOPTIONfor"passwd"
postgres=>deletefrom passwd;
ERROR:  permission denied for relation passwd
postgres=>insertinto passwd (user_name)values('xxx');
ERROR:  permission denied for relation passwd
-- Alice can change her own password; RLS silently prevents updating other rows
postgres=>update passwd set pwhash ='abc';UPDATE1

Todas las políticas construidas hasta ahora han sido políticas permisivas, lo que significa que cuando se aplican múltiples políticas, se combinan utilizando la O Operador booleano. Si bien las políticas permisivas se pueden construir para permitir solo el acceso a las filas en los casos previstos, puede ser más sencillo combinar políticas permisivas con políticas restrictivas (que los registros deben aprobar y que se combinan utilizando el Y Operador booleano). Sobre la base del ejemplo anterior, agregamos una política restrictiva para requerir que el administrador esté conectado a través de un socket Unix local para acceder a los registros del passwd mesa:

CREATE POLICY admin_local_only ON passwd AS RESTRICTIVE TO admin
    USING(pg_catalog.inet_client_addr()ISNULL);

Entonces podemos ver que un administrador que se conecta a través de una red no verá ningún registro, debido a la política restrictiva:

=>SELECTcurrent_user;current_user--------------
 admin
(1row)=>select inet_client_addr();
 inet_client_addr 
------------------127.0.0.1(1row)=>SELECTcurrent_user;current_user--------------
 admin
(1row)=>TABLE passwd;
 user_name | pwhash | uid | gid | real_name | home_phone | extra_info | home_dir | shell
-----------+--------+-----+-----+-----------+------------+------------+----------+-------(0rows)=>UPDATE passwd set pwhash =NULL;UPDATE0

Las verificaciones de integridad referencial, como las restricciones de clave única o primaria y las referencias de clave externa, siempre omiten la seguridad de las filas para garantizar que se mantenga la integridad de los datos. Se debe tener cuidado al desarrollar esquemas y políticas de nivel de fila para evitar canal encubierto filtraciones de información a través de tales controles de integridad referencial.

En algunos contextos, es importante asegurarse de que no se aplique la seguridad de fila. Por ejemplo, al realizar una copia de seguridad, podría ser desastroso si la seguridad de las filas provocara silenciosamente la omisión de algunas filas de la copia de seguridad. En tal situación, puede establecer el parámetro de configuración row_security en off. Esto en sí mismo no evita la seguridad de las filas; lo que hace es lanzar un error si los resultados de alguna consulta se filtran por una política. A continuación, se puede investigar y corregir el motivo del error.

En los ejemplos anteriores, las expresiones de política consideran solo los valores actuales en la fila para acceder o actualizar. Este es el caso más simple y de mejor rendimiento; cuando sea posible, es mejor diseñar aplicaciones de seguridad de filas para que funcionen de esta manera. Si es necesario consultar otras filas u otras tablas para tomar una decisión de política, eso se puede lograr utilizando sub-SELECTs, o funciones que contienen SELECTs, en las expresiones de política. Sin embargo, tenga en cuenta que dichos accesos pueden crear condiciones de carrera que podrían permitir la filtración de información si no se tiene cuidado. Como ejemplo, considere el siguiente diseño de tabla:

-- definition of privilege groupsCREATETABLE groups (group_id intPRIMARYKEY,
                     group_name textNOTNULL);INSERTINTO groups VALUES(1,'low'),(2,'medium'),(5,'high');GRANTALLON groups TO alice;-- alice is the administratorGRANTSELECTON groups TOpublic;-- definition of users' privilege levelsCREATETABLE users (user_name textPRIMARYKEY,
                    group_id intNOTNULLREFERENCES groups);INSERTINTO users VALUES('alice',5),('bob',2),('mallory',2);GRANTALLON users TO alice;GRANTSELECTON users TOpublic;-- table holding the information to be protectedCREATETABLE information (info text,
                          group_id intNOTNULLREFERENCES groups);INSERTINTO information VALUES('barely secret',1),('slightly secret',2),('very secret',5);ALTERTABLE information ENABLEROWLEVEL SECURITY;-- a row should be visible to/updatable by users whose security group_id is-- greater than or equal to the row's group_idCREATE POLICY fp_s ON information FORSELECTUSING(group_id <=(SELECT group_id FROM users WHERE user_name =current_user));CREATE POLICY fp_u ON information FORUPDATEUSING(group_id <=(SELECT group_id FROM users WHERE user_name =current_user));-- we rely only on RLS to protect the information tableGRANTALLON information TOpublic;

Ahora suponga que alice desea cambiar el un poco secreto información, pero decide que mallory no se le debe confiar el nuevo contenido de esa fila, por lo que lo hace:

BEGIN;UPDATE users SET group_id =1WHERE user_name ='mallory';UPDATE information SET info ='secret from mallory'WHERE group_id =2;COMMIT;

Eso parece seguro; no hay ventana donde mallory debería poder ver el secreto de mallory cuerda. Sin embargo, aquí hay una condición de carrera. Si mallory está haciendo al mismo tiempo, digamos,

SELECT*FROM information WHERE group_id =2FORUPDATE;

y su transacción está en READ COMMITTED modo, es posible que ella vea secreto de mallory. Eso sucede si su transacción llega al information fila justo después alicehace. Bloquea esperando alicepara confirmar la transacción, luego recupera el contenido de la fila actualizado gracias al FOR UPDATE cláusula. Sin embargo, lo hace no buscar una fila actualizada para el implícito SELECT de users, porque ese sub-SELECT no tenía FOR UPDATE; en cambio el users La fila se lee con la instantánea tomada al comienzo de la consulta. Por lo tanto, la expresión de política prueba el antiguo valor de mallorynivel de privilegio y le permite ver la fila actualizada.

Hay varias formas de solucionar este problema. Una respuesta simple es usar SELECT ... FOR SHARE en sub-SELECTs en las políticas de seguridad de la fila. Sin embargo, eso requiere otorgar UPDATE privilegio en la tabla referenciada (aquí users) a los usuarios afectados, lo que podría resultar indeseable. (Pero se podría aplicar otra política de seguridad de fila para evitar que realmente ejerzan ese privilegio; o el sub-SELECT podría estar incrustado en una función de definición de seguridad). Además, el uso concurrente de bloqueos de filas compartidas en la tabla a la que se hace referencia podría plantear un problema de rendimiento, especialmente si las actualizaciones son frecuentes. Otra solución, práctica si las actualizaciones de la tabla referenciada son poco frecuentes, es realizar un bloqueo exclusivo en la tabla referenciada al actualizarla, de modo que ninguna transacción concurrente pueda estar examinando valores de fila antiguos. O simplemente se puede esperar a que finalicen todas las transacciones simultáneas después de realizar una actualización de la tabla a la que se hace referencia y antes de realizar cambios que dependan de la nueva situación de seguridad.

Para obtener detalles adicionales, consulte CREAR POLÍTICA y ALTERAR TABLA.

Anterior Hasta próximo
5.7. Privilegios Hogar 5.9. Esquemas