PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Как работят изгледите на PostgreSQL security_barrier?

Може да сте виждали добавена поддръжка за security_barrier изгледи в PostgreSQL 9.2. Разглеждах този код с цел добавяне на поддръжка за автоматична актуализация за тях като част от напредването на работата по сигурността на ниво ред за проекта AXLE и реших да използвам шанса да обясня как работят.

Робърт вече обясни защо са полезни и от какво предпазват. (Оказва се, че това също се обсъжда в новото в 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 =on , debug_print_rewritten =включено и debug_print_plan =on в 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, И и ИЛИ не са поръчани. Оптимизаторът/изпълнителят са свободни да стартират всеки клон, който смятат, че е по-вероятно да им даде бърз отговор и евентуално да им позволи да избягват да изпълняват другите клонове. Така че, ако планиращият смята, че n <4 е много по-бързо от n % 2 =1 първо ще оцени това. Изглежда безобидно, нали? Опитайте:

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)

Опа! Както можете да видите, предоставената от потребителя предикатна функция се считаше за по-евтина за изпълнение от другите тестове, така че беше премината всеки ред, преди предикатът на изгледа да го изключи. Злонамерена функция може да използва същия трик, за да копира реда.

бариера_за_сигурност изгледи

бариера_за_сигурност изгледите коригират това, като принудят квалификаторите на изгледа да се изпълняват първи, преди да се изпълнят предоставените от потребителя квалификатори. Вместо да разширяват изгледа и да добавят каквито и да е квалификатори на изглед към външната заявка, те заменят препратката към изгледа с подзаявка. Тази подзаявка има бариера_за_сигурност флаг, зададен на неговия запис в таблицата с диапазони, който казва на оптимизатора, че не трябва да изравнява подзаявката или да изтласква външните условия на заявка надолу в нея, както би било при нормална подзаявка.

Така че с изглед на бариера за сигурност:

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)

Планът на заявката трябва да ви каже какво се случва, въпреки че не показва атрибута на бариера за сигурност в изхода за обяснение. Вложената подзаявка принуждава сканиране на t с квалификатора за изглед, тогава предоставената от потребителя функция се изпълнява върху резултата от подзаявката.

Но. Изчакай секунда. Защо предоставеният от потребителя предикат n <4 също вътре в подзаявката? Това не е ли потенциална дупка в сигурността? Ако n <4 е натиснат надолу, защо не е f_leak(secret) ?

НЕПЕЧАТЕЛЕН оператори и функции

Обяснението за това е, че < операторът е означен с УТЧИВКА . Този атрибут показва, че на даден оператор или функция се вярва, че няма да изтича информация, така че може безопасно да бъде избутана надолу през security_barrier изгледи. По очевидни причини не можете да зададете LEAKPROOF като обикновен потребител:

regress=> ALTER FUNCTION f_leak(text)  LEAKPROOF;
ERROR:  only superuser can define a leakproof function

и суперпотребителят вече може да прави каквото си поиска, така че не е нужно да прибягва до трикове с функции, изпускащи информация, за да преминат през изглед на бариера за сигурност.

Защо не можете да актуализирате security_barrier изгледи

Простите изгледи в PostgreSQL 9.3 се актуализират автоматично, но security_barrier изгледите не се считат за „прости“. Това е така, защото актуализирането на изгледи разчита на възможността да изглади подзаявката за изглед, превръщайки актуализацията в проста актуализация на таблица. Целият смисъл на security_barrier изгледи е за предотвратяване това сплескване. АКТУАЛИЗИРАНЕ в момента не може да работи директно върху подзаявка, така че 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.

Именно това ограничение се интересувам от премахване като част от работата за напредък на сигурността на ниво ред за проекта AXLE. Kohei KaiGai свърши страхотна работа със сигурността на ниво ред и функции като security_barrier и УДЕРЖАВАЩ ТЕЧ. до голяма степен са възникнали от работата му за добавяне на сигурност на ниво ред към PostgreSQL. Следващото предизвикателство е как да се справите с актуализациите на бариера за сигурност сигурно и по начин, който ще бъде поддържан в бъдеще.

Защо подзаявки?

Може би се чудите защо трябва да използваме подзаявки за това. Направих. Кратката версия е, че не е нужно, но ако не използваме подзаявки, вместо това трябва да създадем нови, чувствителни към поръчката варианти на AND и ИЛИ оператори и да научи оптимизатора, че не може да премества условия в тях. Тъй като изгледите вече са разширени като подзаявки, е много по-малко сложно просто да маркирате подзаявките като огради, които блокират изтегляне/натискане надолу.

В PostgreSQL вече има наредена операция за късо съединение – CASE . Проблемът с използването на CASE че не операциите могат да бъдат преместени през границата на CASE , дори УДЕРЖАВАЩ ТЕЧ. нечий. Оптимизаторът също не може да взема решения за използване на индекса въз основа на изрази в CASE срок. Така че, ако използвахме CASE както попитах за -hackers, ние никога не бихме могли да използваме индекс, за да удовлетворим предоставен от потребителя квалификатор.

В кода

бариера_за_сигурност поддръжката беше добавена в 0e4611c0234d89e288a53351f775c59522baed7c . Той беше подобрен с устойчива на теч поддръжка в cd30728fb2ed7c367d545fc14ab850b5fa2a4850 . Кредитите се появяват в бележките за ангажименти. Благодаря на всички участващи.

Изображението на предната страница е Security Barrier от Craig A. Rodway, на Flikr

Изследванията, довели до тези резултати, са получили финансиране от Седмата рамкова програма на Европейския съюз (FP7/2007-2013) по споразумение за отпускане на безвъзмездни средства № 318633


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да определя последния ден от предишния месец с помощта на PostgreSQL?

  2. Конвертирайте SQLITE SQL дъмп файл в POSTGRESQL

  3. Групово/партидна актуализация/внасяне в PostgreSQL

  4. Съпоставете фраза, завършваща на префикс, с пълно текстово търсене

  5. Механизми за физическа репликация в PostgreSQL