Возможно, вы заметили, что в PostgreSQL 9.2 добавлена поддержка представлений security_barrier. Я рассматривал этот код с целью добавить для них поддержку автоматического обновления в рамках разработки безопасности на уровне строк для проект ОСЬ , и я решил попытаться объяснить, как они работают. Роберт уже объяснил в чем польза таких представлений и от чего они защищают (кроме того, об этом также говорилось в " Что нового в PostgreSQL 9.2 ").
Теперь хотелось бы перейти к Как они работают и обсуждают, как представления security_barrier взаимодействуют с автоматически обновляемыми представлениями.
Регулярные просмотры
Простое регулярное представление расширяется в виде макроса в виде подзапроса, который затем обычно оптимизируется путем удаления его предиката и добавления его к условиям содержащего запроса.Это может стать яснее на примере.
Учитывая таблицу:
и представительство:CREATE TABLE t AS SELECT n, 'secret'||n AS secret FROM generate_series(1,20) n;
CREATE VIEW t_odd AS SELECT n, secret FROM t WHERE n % 2 = 1;
запрос типа:
SELECT * FROM t_odd WHERE n < 4
будет преобразовано внутри обработчика запроса в следующий вид:
SELECT * FROM (SELECT * FROM t WHERE n % 2 = 1) t_odd WHERE n < 4
который оптимизатор затем превратит в запрос, выполняемый сразу, переместив подзапрос и условия WHERE во внешний запрос:
SELECT * FROM t t_odd WHERE (n % 2 = 1) AND (n < 4)
Вы не сможете напрямую видеть мгновенные запросы, и они никогда не существуют в виде реального SQL, но вы можете увидеть процесс, включив debug_print_parse = включено , debug_print_rewrite = включено И debug_print_plan = включено в postgresql.conf. Я не буду воспроизводить здесь деревья разбора и планирования, поскольку они достаточно громоздки и их можно легко сгенерировать на основе приведенных выше примеров.
Проблема с использованием представлений в целях безопасности.
Вы можете подумать, что предоставление кому-либо доступа к представлению без доступа к самой таблице помешает им видеть четные строки.
На самом деле это похоже на правду: regress=> SELECT * FROM t_odd WHERE n < 4;
n | secret
---+---------
1 | secret1
3 | secret3
(2 rows)
но если посмотреть на план, то можно увидеть потенциальную проблему: regress=> EXPLAIN SELECT * FROM t_odd WHERE n < 4;
QUERY PLAN
---------------------------------------------------
Seq Scan on t (cost=0.00.31.53 rows=2 width=36)
Filter: ((n < 4) AND ((n % 2) = 1))
(2 rows)
Подзапрос представления был оптимизирован, а его определители были перенесены во внешний запрос.
В SQL операторы AND и OR не упорядочиваются.
Оптимизатор/исполнитель имеет полную свободу выбора ветки для запуска, которую он считает более быстрой с точки зрения выдачи ответа, и может позволить ему избежать запуска других ветвей.
Те.
если планировщик считает, что н < 4 гораздо быстрее, чем п% 2 , он запустит его первым.
Выглядит безобидно, правда? Пытаться: regress=> CREATE OR REPLACE FUNCTION f_leak(text) RETURNS boolean AS $$
BEGIN
RAISE NOTICE 'Secret is: %',$1;
RETURN true;
END;
$$ COST 1 LANGUAGE plpgsql;
regress=> SELECT * FROM t_odd WHERE f_leak(secret) AND n < 4;
NOTICE: Secret is: secret1
NOTICE: Secret is: secret2
NOTICE: Secret is: secret3
NOTICE: Secret is: secret4
NOTICE: Secret is: secret5
NOTICE: Secret is: secret6
NOTICE: Secret is: secret7
NOTICE: Secret is: secret8
NOTICE: Secret is: secret9
NOTICE: Secret is: secret10
NOTICE: Secret is: secret11
NOTICE: Secret is: secret12
NOTICE: Secret is: secret13
NOTICE: Secret is: secret14
NOTICE: Secret is: secret15
NOTICE: Secret is: secret16
NOTICE: Secret is: secret17
NOTICE: Secret is: secret18
NOTICE: Secret is: secret19
NOTICE: Secret is: secret20
n | secret
---+---------
1 | secret1
3 | secret3
(2 rows)
regress=> EXPLAIN SELECT * FROM t_odd WHERE f_leak(secret) AND n < 4;
QUERY PLAN
----------------------------------------------------------
Seq Scan on t (cost=0.00.34.60 rows=1 width=36)
Filter: (f_leak(secret) AND (n < 4) AND ((n % 2) = 1))
(2 rows)
Упс! Как видите, выполнение функции с предоставленным пользователем предикатом считалось более дешевым, чем другие тесты, поэтому она пропускала все строки, прежде чем предикат представления исключил те, которые не совпадали.
Вредоносная функция может использовать тот же трюк для копирования строк.
просмотры security_barrier
Представления Security_barrier исправляют эту проблему, заставляя сначала выполняться условия представления, прежде чем будут применены любые условия, созданные пользователем.Вместо того, чтобы расширять представление и помещать любые условия представления во внешний запрос, они заменяют ссылку на представление подзапросом.
Для этого подзапроса установлен флаг security_barrier во всем диапазоне его вхождений в таблицу, который сообщает оптимизатору, что ему не следует трогать подзапрос или удалять из него условия, как это обычно происходит.
Таким образом, представление с защитным барьером: CREATE VIEW t_odd_sb WITH (security_barrier) AS SELECT n, secret FROM t WHERE n % 2 = 1;
мы получим: regress=> SELECT * FROM t_odd_sb WHERE f_leak(secret) AND n < 4;
NOTICE: Secret is: secret1
NOTICE: Secret is: secret3
n | secret
---+---------
1 | secret1
3 | secret3
(2 rows)
regress=> EXPLAIN SELECT * FROM t_odd_sb WHERE f_leak(secret) AND n < 4;
QUERY PLAN
---------------------------------------------------------------
Subquery Scan on t_odd_sb (cost=0.00.31.55 rows=1 width=36)
Filter: f_leak(t_odd_sb.secret)
-> Seq Scan on t (cost=0.00.31.53 rows=2 width=36)
Filter: ((n < 4) AND ((n % 2) = 1))
(4 rows)
План выполнения запроса должен сообщать вам, что происходит, хотя в выводе объяснения атрибут барьера безопасности не отображается.
Вложенный подзапрос заставляет сканировать т с условиями подзапроса представления, после чего на полученных данных выполняются условия написанной пользователем функции.
Но.
Всего секунду.
Почему пользователь применил предикат н < 4 также появляется в подзапросе? Разве это не потенциальная дыра в безопасности? Если н < 4 опущено, то почему бы и нет f_leak (секрет) ?
Операторы и функции LEAKPROOF
Это объясняется тем, что оператор < отмечен как ДОКАЗАТЕЛЬСТВО УТЕЧКИ .Этот атрибут сигнализирует о том, что этому оператору или функции доверяют для предотвращения утечки информации; соответственно, его можно смело применять к Security_barrier идеи.
ПО понятным причинам вы не сможете установить атрибут ДОКАЗАТЕЛЬСТВО УТЕЧКИ как обычный пользователь: regress=> ALTER FUNCTION f_leak(text) LEAKPROOF;
ERROR: only superuser can define a leakproof function
суперпользователи могут делать все, что хотят, и им не придется прибегать к уловкам утечки, чтобы преодолеть барьер убеждений.
Почему вы не можете обновить security_barrier представления?
Обычные представления в PostgreSQL 9.3 автоматически обновляется , но представления security_barrier не подразумевают «простоту».Это связано с тем, что обновления представления полагаются на возможность удаления подзапроса представления, превращая обновление в обычное обновление таблицы.
Вся суть представлений security_barrier заключается в том, чтобы не допустить исключения этого условия представления.
UPDATE в настоящее время не может работать напрямую с подзапросом, поэтому PostgreSQL отклонит любую попытку обновить представление security_barrier: regress = > UPDATE t_odd
SET
secret = 'secret_haha' || n;
UPDATE 10 regress = > UPDATE t_odd_sb
SET
secret = 'secret_haha' || n;
ERROR: cannot UPDATE VIEW "t_odd_sb" DETAIL: SECURITY - barrier views ARE NOT automatically updatable. HINT: TO ENABLE updating the VIEW,
provide an INSTEAD OF UPDATE TRIGGER
OR an unconditional ON UPDATE DO INSTEAD RULE.
Это именно то ограничение, которое я хочу устранить в рамках работы по разработке безопасности на уровне строк для проект ОСЬ .
Кохей КайГай проделал огромную работу над безопасностью на уровне строк, и такие вещи, как Security_barrier и LEAKPROOF, во многом выросли из его работы по добавлению безопасности на уровне строк в PostgreSQL. Следующая задача заключается в том, как обеспечить безопасное обновление защитного барьера таким образом, чтобы его можно было использовать в будущем.
Почему подзапросы?
Вам может быть интересно, почему мы используем для этого подзапросы.Я размышлял.
Вкратце, нам не следует этого делать, но если мы не будем использовать подзапросы, нам придется вместо этого создавать новые, чувствительные к сортировке варианты операторов AND и OR и учить оптимизатор, что он не может выполнять условия через их.
Поскольку представления уже расширены подзапросами, гораздо проще пометить подзапросы как ограждения, предотвращающие извлечение/добавление в них данных.
В PostgreSQL уже есть упрощенная упорядоченная операция — СЛУЧАЙ .
Проблема с использованием СЛУЧАЙ заключается в том, что никакие транзакции не могут пересекать границы СЛУЧАЙ , даже ДОКАЗАТЕЛЬСТВО УТЕЧКИ .
Точно так же, как оптимизатор не может принимать решения об использовании индексов? на основе выражений внутри блока CASE. Поэтому, если бы мы использовали CASE, как я здесь спрашивал, мы бы никогда не смогли использовать индекс для удовлетворения условия, заданного пользователем.
В коде
добавлена поддержка security_barrier 0e4611c0234d89e288a53351f775c59522baed7c и улучшена поддержкой LEAKPROOF в cd30728fb2ed7c367d545fc14ab850b5fa2a4850 .Слова благодарности отправляются в примечания к коммитам.
Спасибо всем, кто принял участие.
ПС.
Статья относительно старая, но важна как введение к переводу следующей статьи.
Теги: #postgresql #security #sql #leakproof #postgresql #sql
-
Данные Водителя
19 Oct, 24 -
3Dmark Vantage: Новый Общий Рекорд
19 Oct, 24 -
Skype: Эксперимент С Телефонной Будкой
19 Oct, 24 -
Идея Социальной Службы. Помогите Советом
19 Oct, 24 -
Задача Визуализации
19 Oct, 24 -
Принципы Достижения Реальности (Часть 1)
19 Oct, 24