Как Представления Security_Barrier Работают В Postgresql

Возможно, вы заметили, что в 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

Вместе с данным постом часто просматривают: