PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Jak działają widoki bariery bezpieczeństwa PostgreSQL?

Być może widziałeś wsparcie dodane dla security_barrier widoki w PostgreSQL 9.2. Przyglądałem się temu kodowi z myślą o dodaniu obsługi automatycznych aktualizacji w ramach postępu prac nad bezpieczeństwem na poziomie wiersza w projekcie AXLE i pomyślałem, że skorzystam z okazji, aby wyjaśnić, jak one działają.

Robert wyjaśnił już, dlaczego są przydatne i przed czym chronią. (Okazuje się, że jest to również omówione w nowościach w wersji 9.2). Teraz chcę przejść do jak pracują i omawiają, jak security_barrier widoki współdziałają z automatycznie aktualizowanymi widokami.

Zwykłe widoki

Zwykły widok prosty jest rozwijany w sposób podobny do makra jako podzapytanie, które jest następnie zwykle optymalizowane przez wyciągnięcie predykatu w górę i dodanie go do wartości zapytania zawierającego. To może mieć więcej sensu na przykładzie. Podana tabela:

CREATE TABLE t AS SELECT n, 'secret'||n AS secret FROM generate_series(1,20) n;

i zobacz:

CREATE VIEW t_odd AS SELECT n, secret FROM t WHERE n % 2 = 1;

zapytanie takie jak:

SELECT * FROM t_odd WHERE n < 4

jest rozwijany w widoku wewnątrz programu do ponownego zapisywania zapytań do postaci drzewa analizy zapytania, takiego jak:

SELECT * FROM (SELECT * FROM t WHERE n % 2 = 1) t_odd WHERE n < 4

które optymalizator następnie spłaszcza w jednoprzebiegowe zapytanie, eliminując podzapytanie i dołączając WHERE warunki klauzuli do zapytania zewnętrznego, tworząc:

SELECT * FROM t t_odd WHERE (n % 2 = 1) AND (n < 4)

Mimo że nie możesz zobaczyć zapytań pośrednich bezpośrednio i nigdy nie istnieją jako prawdziwy SQL, możesz zaobserwować ten proces, włączając debug_print_parse =on , debug_print_rewritten =wł. i debug_print_plan =wł. w postgresql.conf . Nie powiem tutaj analizować i planować drzew, ponieważ są one dość duże i łatwe do wygenerowania na podstawie powyższych przykładów.

Problem z używaniem widoków do celów bezpieczeństwa

Można by pomyśleć, że przyznanie komuś dostępu do widoku bez przyznania mu dostępu do tabeli bazowej uniemożliwiłoby mu oglądanie wierszy o numerach parzystych. Początkowo wygląda na to, że to prawda:

regress=> SELECT * FROM t_odd WHERE n < 4;
 n | secret  
---+---------
 1 | secret1
 3 | secret3
(2 rows)

ale kiedy spojrzysz na plan, możesz zobaczyć potencjalny problem:

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)

Podzapytanie widoku zostało zoptymalizowane, a kwalifikatory widoku zostały dołączone bezpośrednio do zapytania zewnętrznego.

W SQL ORAZ i LUB nie są zamawiane. Optymalizator/executor może uruchomić dowolną gałąź, która według nich jest bardziej prawdopodobna, aby dać im szybką odpowiedź i być może pozwoli im uniknąć uruchamiania innych gałęzi. Więc jeśli planista uważa, że ​​n <4 jest znacznie szybszy niż n % 2 =1 najpierw to oceni. Wydaje się nieszkodliwy, prawda? Wypróbuj:

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)

Ups! Jak widać, funkcja predykatu dostarczona przez użytkownika została uznana za tańszą do uruchomienia niż inne testy, więc była przekazywana w każdym wierszu, zanim predykat widoku ją wykluczył. Złośliwa funkcja może użyć tej samej sztuczki do skopiowania wiersza.

bariera_zabezpieczeń widoki

bariera_zabezpieczeń widoki naprawiają to, wymuszając wykonanie kwalifikatorów w widoku jako pierwsze, przed uruchomieniem jakichkolwiek kwalifikatorów podanych przez użytkownika. Zamiast rozszerzać widok i dołączać wszelkie kwalifikatory widoku do zewnętrznego zapytania, zastępują odwołanie do widoku podzapytaniem. To podzapytanie ma security_barrier flaga ustawiona we wpisie tabeli zakresów, która mówi optymalizatorowi, że nie powinien spłaszczać podzapytania ani wpychać do niego zewnętrznych warunków zapytania, tak jak w przypadku normalnego podzapytania.

Tak więc z widokiem bariery bezpieczeństwa:

CREATE VIEW t_odd_sb WITH (security_barrier) AS SELECT n, secret FROM t WHERE n % 2 = 1;

otrzymujemy:

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)

Plan zapytania powinien informować o tym, co się dzieje, chociaż nie pokazuje atrybutu bariery bezpieczeństwa w danych wyjściowych wyjaśniania. Zagnieżdżone podzapytanie wymusza skanowanie t z kwalifikatorem widoku, funkcja dostarczona przez użytkownika działa na wyniku podzapytania.

Ale. Poczekaj sekundkę. Dlaczego predykat dostarczony przez użytkownika to n <4 również w podzapytaniu? Czy to nie jest potencjalna luka w zabezpieczeniach? Jeśli n <4 jest naciskany, dlaczego nie jest f_leak(secret) ?

SZCZELNE operatory i funkcje

Wyjaśnieniem tego jest to, że < operator jest oznaczony jako LEAKPROOF . Ten atrybut wskazuje, że operatorowi lub funkcji można zaufać, że nie ujawni informacji, więc można je bezpiecznie przesunąć przez security_barrier wyświetlenia. Z oczywistych powodów nie możesz ustawić LEAKPROOF jako zwykły użytkownik:

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

a superużytkownik może już robić, co chce, więc nie musi uciekać się do sztuczek z funkcjami wyciekującymi informacje, aby ominąć widok bariery bezpieczeństwa.

Dlaczego nie możesz zaktualizować security_barrier widoki

Proste widoki w PostgreSQL 9.3 są automatycznie aktualizowane, ale security_barrier widoki nie są uważane za „proste”. Dzieje się tak, ponieważ aktualizowanie widoków opiera się na możliwości spłaszczenia podzapytania widoku, zmieniając aktualizację w prostą aktualizację tabeli. Cały punkt security_barrier widoki to zapobieganie to spłaszczenie. AKTUALIZACJA nie może obecnie działać bezpośrednio na podzapytaniu, więc PostgreSQL odrzuci każdą próbę aktualizacji security_barrier widok:

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.

To właśnie to ograniczenie interesuje mnie zniesienie w ramach prac nad postępem zabezpieczeń na poziomie wiersza dla projektu AXLE. Kohei KaiGai wykonał świetną robotę z zabezpieczeniami na poziomie wiersza i funkcjami takimi jak security_barrier i SZCZELNE w dużej mierze powstały w wyniku jego pracy nad dodaniem zabezpieczeń na poziomie wiersza do PostgreSQL. Kolejnym wyzwaniem jest bezpieczne radzenie sobie z aktualizacjami bariery bezpieczeństwa w sposób, który będzie można utrzymać w przyszłości.

Dlaczego podzapytania?

Być może zastanawiasz się, dlaczego musimy do tego używać podzapytań. Zrobiłem. Krótka wersja jest taka, że ​​nie musimy tego robić, ale jeśli nie używamy podzapytań, musimy zamiast tego utworzyć nowe warianty zależne od kolejności ORAZ i LUB operatorów i naucz optymalizatora, że ​​nie może przenosić warunków przez nich. Ponieważ widoki są już rozwinięte jako podzapytania, znacznie mniej skomplikowane jest oznaczanie podzapytań jako ogrodzeń, które blokują podciąganie/dociskanie.

Jednak w PostgreSQL istnieje już uporządkowana operacja zwarcia — CASE . Problem z użyciem CASE że nie operacje mogą być przenoszone przez granicę CASE , nawet SZCZELNE te. Optymalizator nie może również podejmować decyzji dotyczących użycia indeksu na podstawie wyrażeń zawartych w CASE termin. Więc jeśli użyliśmy CASE jak pytałem o -hackerów, nigdy nie moglibyśmy użyć indeksu, aby spełnić kwalifikator podany przez użytkownika.

W kodzie

bariera_zabezpieczeń wsparcie zostało dodane w 0e4611c0234d89e288a53351f775c59522baed7c . Został wzbogacony o szczelne wsparcie w cd30728fb2ed7c367d545fc14ab850b5fa2a4850 . Kredyty pojawiają się w notatkach dotyczących zatwierdzenia. Dziękuję wszystkim zaangażowanym.

Obraz funkcji na pierwszej stronie to Bariera bezpieczeństwa autorstwa Craiga A. Rodwaya, na Flikr

Badania prowadzące do tych wyników otrzymały finansowanie z Siódmego Programu Ramowego Unii Europejskiej (FP7/2007-2013) w ramach umowy o grant nr 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. Jak zmienić typ kolumny w Heroku?

  2. Jak działa funkcja Setseed() w PostgreSQL

  3. Oblicz punkt 50 mil (północ, 45% NE, 45% SW)

  4. Jak zainstalować Haproxy i Keepalived

  5. dobry klient postgresql dla systemu Windows?