mirror of
https://github.com/postgres/postgres.git
synced 2025-07-02 09:02:37 +03:00
Add security checks to the multivariate MCV estimation code.
The multivariate MCV estimation code may run user-defined operators on the values in the MCV list, which means that those operators may potentially leak the values from the MCV list. Guard against leaking data to unprivileged users by checking that the user has SELECT privileges on the table or all of the columns referred to by the statistics. Additionally, if there are any securityQuals on the RTE (either due to RLS policies on the table, or accessing the table via a security barrier view), not all rows may be visible to the current user, even if they have table or column privileges. Thus we further insist that the operator be leakproof in this case. Dean Rasheed, reviewed by Tomas Vondra. Discussion: https://postgr.es/m/CAEZATCUhT9rt7Ui=Vdx4N==VV5XOK5dsXfnGgVOz_JhAicB=ZA@mail.gmail.com
This commit is contained in:
@ -696,3 +696,63 @@ SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE NOT a AND
|
||||
1 | 0
|
||||
(1 row)
|
||||
|
||||
-- Permission tests. Users should not be able to see specific data values in
|
||||
-- the extended statistics, if they lack permission to see those values in
|
||||
-- the underlying table.
|
||||
--
|
||||
-- Currently this is only relevant for MCV stats.
|
||||
CREATE TABLE priv_test_tbl (
|
||||
a int,
|
||||
b int
|
||||
);
|
||||
INSERT INTO priv_test_tbl
|
||||
SELECT mod(i,5), mod(i,10) FROM generate_series(1,100) s(i);
|
||||
CREATE STATISTICS priv_test_stats (mcv) ON a, b
|
||||
FROM priv_test_tbl;
|
||||
ANALYZE priv_test_tbl;
|
||||
-- User with no access
|
||||
CREATE USER regress_stats_user1;
|
||||
SET SESSION AUTHORIZATION regress_stats_user1;
|
||||
SELECT * FROM priv_test_tbl; -- Permission denied
|
||||
ERROR: permission denied for table priv_test_tbl
|
||||
-- Attempt to gain access using a leaky operator
|
||||
CREATE FUNCTION op_leak(int, int) RETURNS bool
|
||||
AS 'BEGIN RAISE NOTICE ''op_leak => %, %'', $1, $2; RETURN $1 < $2; END'
|
||||
LANGUAGE plpgsql;
|
||||
CREATE OPERATOR <<< (procedure = op_leak, leftarg = int, rightarg = int,
|
||||
restrict = scalarltsel);
|
||||
SELECT * FROM priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Permission denied
|
||||
ERROR: permission denied for table priv_test_tbl
|
||||
DELETE FROM priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Permission denied
|
||||
ERROR: permission denied for table priv_test_tbl
|
||||
-- Grant access via a security barrier view, but hide all data
|
||||
RESET SESSION AUTHORIZATION;
|
||||
CREATE VIEW priv_test_view WITH (security_barrier=true)
|
||||
AS SELECT * FROM priv_test_tbl WHERE false;
|
||||
GRANT SELECT, DELETE ON priv_test_view TO regress_stats_user1;
|
||||
-- Should now have access via the view, but see nothing and leak nothing
|
||||
SET SESSION AUTHORIZATION regress_stats_user1;
|
||||
SELECT * FROM priv_test_view WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
||||
a | b
|
||||
---+---
|
||||
(0 rows)
|
||||
|
||||
DELETE FROM priv_test_view WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
||||
-- Grant table access, but hide all data with RLS
|
||||
RESET SESSION AUTHORIZATION;
|
||||
ALTER TABLE priv_test_tbl ENABLE ROW LEVEL SECURITY;
|
||||
GRANT SELECT, DELETE ON priv_test_tbl TO regress_stats_user1;
|
||||
-- Should now have direct table access, but see nothing and leak nothing
|
||||
SET SESSION AUTHORIZATION regress_stats_user1;
|
||||
SELECT * FROM priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
||||
a | b
|
||||
---+---
|
||||
(0 rows)
|
||||
|
||||
DELETE FROM priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
||||
-- Tidy up
|
||||
DROP OPERATOR <<< (int, int);
|
||||
DROP FUNCTION op_leak(int, int);
|
||||
RESET SESSION AUTHORIZATION;
|
||||
DROP VIEW priv_test_view;
|
||||
DROP TABLE priv_test_tbl;
|
||||
|
@ -446,3 +446,63 @@ SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE NOT a AND
|
||||
SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE NOT a AND NOT b AND c');
|
||||
|
||||
SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE NOT a AND b AND NOT c');
|
||||
|
||||
-- Permission tests. Users should not be able to see specific data values in
|
||||
-- the extended statistics, if they lack permission to see those values in
|
||||
-- the underlying table.
|
||||
--
|
||||
-- Currently this is only relevant for MCV stats.
|
||||
CREATE TABLE priv_test_tbl (
|
||||
a int,
|
||||
b int
|
||||
);
|
||||
|
||||
INSERT INTO priv_test_tbl
|
||||
SELECT mod(i,5), mod(i,10) FROM generate_series(1,100) s(i);
|
||||
|
||||
CREATE STATISTICS priv_test_stats (mcv) ON a, b
|
||||
FROM priv_test_tbl;
|
||||
|
||||
ANALYZE priv_test_tbl;
|
||||
|
||||
-- User with no access
|
||||
CREATE USER regress_stats_user1;
|
||||
SET SESSION AUTHORIZATION regress_stats_user1;
|
||||
SELECT * FROM priv_test_tbl; -- Permission denied
|
||||
|
||||
-- Attempt to gain access using a leaky operator
|
||||
CREATE FUNCTION op_leak(int, int) RETURNS bool
|
||||
AS 'BEGIN RAISE NOTICE ''op_leak => %, %'', $1, $2; RETURN $1 < $2; END'
|
||||
LANGUAGE plpgsql;
|
||||
CREATE OPERATOR <<< (procedure = op_leak, leftarg = int, rightarg = int,
|
||||
restrict = scalarltsel);
|
||||
SELECT * FROM priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Permission denied
|
||||
DELETE FROM priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Permission denied
|
||||
|
||||
-- Grant access via a security barrier view, but hide all data
|
||||
RESET SESSION AUTHORIZATION;
|
||||
CREATE VIEW priv_test_view WITH (security_barrier=true)
|
||||
AS SELECT * FROM priv_test_tbl WHERE false;
|
||||
GRANT SELECT, DELETE ON priv_test_view TO regress_stats_user1;
|
||||
|
||||
-- Should now have access via the view, but see nothing and leak nothing
|
||||
SET SESSION AUTHORIZATION regress_stats_user1;
|
||||
SELECT * FROM priv_test_view WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
||||
DELETE FROM priv_test_view WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
||||
|
||||
-- Grant table access, but hide all data with RLS
|
||||
RESET SESSION AUTHORIZATION;
|
||||
ALTER TABLE priv_test_tbl ENABLE ROW LEVEL SECURITY;
|
||||
GRANT SELECT, DELETE ON priv_test_tbl TO regress_stats_user1;
|
||||
|
||||
-- Should now have direct table access, but see nothing and leak nothing
|
||||
SET SESSION AUTHORIZATION regress_stats_user1;
|
||||
SELECT * FROM priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
||||
DELETE FROM priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
||||
|
||||
-- Tidy up
|
||||
DROP OPERATOR <<< (int, int);
|
||||
DROP FUNCTION op_leak(int, int);
|
||||
RESET SESSION AUTHORIZATION;
|
||||
DROP VIEW priv_test_view;
|
||||
DROP TABLE priv_test_tbl;
|
||||
|
Reference in New Issue
Block a user