>From 63e90965e8ce0d89113002c6df283761ffc6adfc Mon Sep 17 00:00:00 2001 From: Craig Ringer Date: Mon, 20 Jan 2014 13:50:37 +0800 Subject: [PATCH 10/10] RLS: Supplimental docs --- doc/src/sgml/user-manag.sgml | 145 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/doc/src/sgml/user-manag.sgml b/doc/src/sgml/user-manag.sgml index 177ac7a..c764189 100644 --- a/doc/src/sgml/user-manag.sgml +++ b/doc/src/sgml/user-manag.sgml @@ -439,4 +439,149 @@ DROP ROLE name; + + Row-security + + PostgreSQL v9.4 or later provides + row-security feature, like several commercial database + management system. It allows table owner to assign a particular + condition that performs as a security policy of the table; only + rows that satisfies the condition should be visible, except for + a case when superuser runs queries. + + + Row-security policy can be set using SET ROW SECURITY + command of statement, as an expression + form that returns a value of type boolean. This expression can contain + references to columns of the relation, so it enables to construct + arbitrary rule to make access control decision based on contents of + each rows. + + + For example, the following customer table has + uname field to store user name, and it assume + we don't want to expose any properties of other customers. + The following command set current_user = uname + as row-security policy on the customer table. + +postgres=> ALTER TABLE customer SET ROW SECURITY + FOR ALL TO (current_user = uname); +ALTER TABLE + + command shows how row-security policy + works on the supplied query. + +postgres=> EXPLAIN(costs off) SELECT * FROM customer WHERE f_leak(upasswd); + QUERY PLAN +-------------------------------------------- + Subquery Scan on customer + Filter: f_leak(customer.upasswd) + -> Seq Scan on customer customer_1 + Filter: ("current_user"() = uname) +(4 rows) + + This query execution plan means the preconfigured row-security policy is + implicitly added, and scan plan on the target relation being wrapped up + with a sub-query. + It ensures user given qualifiers, including functions with side effects, + are never executed earlier than the row-security policy regardless of + its cost, except for the cases when these were fully leakproof. + This design helps to tackle the scenario described in + ; that introduces the order to evaluate + qualifiers is significant to keep confidentiality of invisible rows. + + + + On the other hand, this design allows superusers to bypass checks with + row-security. + It ensures pg_dump can obtain a complete set + of database backup, and avoid to execute Trojan horse trap, being injected + as a row-security policy of user-defined table, with privileges of + superuser. + + + + In case of queries on inherited tables, row-security policy of the parent + relation is not applied to child relations. Scope of the row-security + policy is limited to the relation on which it is set. + +postgres=> EXPLAIN(costs off) SELECT * FROM t1 WHERE f_leak(y); + QUERY PLAN +------------------------------------- + Append + -> Subquery Scan on t1 + Filter: f_leak(t1.y) + -> Seq Scan on t1 t1_1 + Filter: ((x % 2) = 0) + -> Seq Scan on t2 + Filter: f_leak(y) + -> Subquery Scan on t3 + Filter: f_leak(t3.y) + -> Seq Scan on t3 t3_1 + Filter: ((x % 2) = 1) +(11 rows) + + In the above example, t1 has inherited + child table t2 and t3, + and row-security policy is set on only t1, + and t3, not t2. + + The row-security policy of t1, x + must be even-number, is appended only t1, + neither t2 nor t3. + On the contrary, t3 has different row-security policy; + x must be odd-number. + + + + Row-security feature also works to queries for writer-operations; such as + , or + commands. + It ensures all the modified rows satisfies configured row-security policy. + The below query tries to update e-mail address of the + customer table, and configured row-security makes sure all the + modified rows's uname field has to match with + current_user. + + +postgres=> EXPLAIN (costs off) UPDATE customer SET email = 'alice@example.com'; + QUERY PLAN +-------------------------------------------------- + Update on customer + -> Subquery Scan on customer + -> Seq Scan on customer customer_1 + Filter: ("current_user"() = uname) +(4 rows) + + + Modification of a certain table is consist of two different stuffs; one + is fetch rows to be modified from the result relation (except for + INSERT command), second is insertion of rows to the + result relation. Usually, before-row trigger or check constraints are + run in the second phase. In addition, row-security policy is also checked + on this stage, to prevent to insert or update rows with unprivileged + values. + + + + Unlike other commercial database systems, we don't have any plan to allow + individual row-security policy for each command type. Even if we want to + perform with difference policy between and + , RETURNING clause can leak the rows + to be invisible using command. + + + + Even though it is not a specific matter in row-security, please be careful + if you plan to use current_user in row-level security policy. + and allows to + switch current user identifier during execution of the query. + Thus, it can fetch the first 100 rows with privilege of alice, + then remaining rows with privilege of bob. If and when query + execution plan contains some kind of materialization and row-security + policy contains current_user, the fetched tuples in + bob's screen might be evaluated according to the privilege of + alice. + + -- 1.8.3.1