sql >> Database teknologi >  >> RDS >> PostgreSQL

Hvordan fungerer PostgreSQL security_barrier views?

Du har måske set understøttelsen tilføjet til security_barrier visninger i PostgreSQL 9.2. Jeg har kigget på den kode med henblik på at tilføje automatisk opdateringsunderstøttelse til dem som en del af det fremskridende sikkerhedsarbejde på rækkeniveau for AXLE-projektet, og jeg tænkte, at jeg ville tage chancen for at forklare, hvordan de fungerer.

Robert har allerede forklaret, hvorfor de er nyttige, og hvad de beskytter mod. (Det viser sig, at det også er diskuteret i det nye i 9.2). Nu vil jeg gå ind på hvordan de arbejder og diskuterer hvordan security_barrier visninger interagerer med visninger, der automatisk kan opdateres.

Normale visninger

En normal simpel visning udvides på en makro-lignende måde som en underforespørgsel, der så sædvanligvis optimeres væk ved at trække sit prædikat op og tilføje det til kvalene for den indeholdende forespørgsel. Det giver måske mere mening med et eksempel. Givet tabel:

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

og se:

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

en forespørgsel som:

SELECT * FROM t_odd WHERE n < 4

er visningsudvidet inde i forespørgselsomskriveren til en parse-trærepræsentation af en forespørgsel som:

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

som optimeringsværktøjet derefter udflader til en enkelt pass-forespørgsel ved at eliminere underforespørgslen og tilføje Hvor klausulvilkår til den ydre forespørgsel, der producerer:

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

Selvom du ikke kan se de mellemliggende forespørgsler direkte, og de aldrig eksisterer som ægte SQL, kan du observere denne proces ved at aktivere debug_print_parse =on , debug_print_rewritten =on og debug_print_plan =on i postgresql.conf . Jeg vil ikke gengive parse- og plantræerne her, da de er ret store og nemme at generere baseret på eksemplerne ovenfor.

Problemet med at bruge visninger til sikkerhed

Du tror måske, at det at give nogen adgang til visningen uden at give dem adgang til den underliggende tabel ville forhindre dem i at se lige rækker. I første omgang ser det ud til, at det er sandt:

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

men når du ser på planen, ser du måske et potentielt 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)

View-underforespørgslen er blevet optimeret væk, med visningens kvalifikationer tilføjet direkte til den ydre forespørgsel.

I SQL, AND og ELLER er ikke bestilt. Optimizeren/eksekutoren kan frit køre, hvilken gren de mener er mere tilbøjelig til at give dem et hurtigt svar og eventuelt lade dem undgå at køre de andre filialer. Så hvis planlæggeren mener, at n <4 er meget hurtigere end n % 2 =1 det vil vurdere det først. Det virker harmløst, ikke? Prøv:

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)

Hov! Som du kan se, blev den brugerleverede prædikatfunktion anset for at være billigere at køre end de andre tests, så den blev bestået hver række, før visningens prædikat havde udelukket det. En ondsindet funktion kunne bruge det samme trick til at kopiere rækken.

sikkerhedsbarriere visninger

sikkerhedsbarriere views løser det ved at tvinge kvalifikationerne på visningen til at blive udført først, før nogen brugerleverede kvalifikationer kører. I stedet for at udvide visningen og tilføje eventuelle visningskvalifikationer til den ydre forespørgsel, erstatter de referencen til visningen med en underforespørgsel. Denne underforespørgsel har sikkerhedsbarrieren flag sat på dens interval-tabel-indgang, som fortæller optimizeren, at den ikke skal udjævne underforespørgslen eller skubbe ydre forespørgselsbetingelser ned i den, som det ville gøre for en normal underforespørgsel.

Så med en sikkerhedsbarrierevisning:

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

vi får:

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)

Forespørgselsplanen skal fortælle dig, hvad der sker, selvom den ikke viser sikkerhedsbarriere-attributten i forklare-output. Den indlejrede underforespørgsel fremtvinger en scanning på t med visningskvalifikationen, så kører den brugerleverede funktion på resultatet af underforespørgslen.

Men. Vent et sekund. Hvorfor er det brugerleverede prædikat n <4 også inde i underforespørgslen? Er det ikke et potentielt sikkerhedshul? Hvis n <4 er presset ned, hvorfor er f_leak(secret) ikke ?

LÆKSIKKER operatører og funktioner

Forklaringen på det er, at < operatør er mærket LEAKPROOF . Denne attribut angiver, at en operatør eller funktion har tillid til ikke at lække information, så den kan skubbes sikkert ned gennem security_barrier visninger. Af indlysende årsager kan du ikke indstille LEAKPROOF som almindelig bruger:

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

og superbrugeren kan allerede gøre, hvad de vil, så de behøver ikke at ty til at spille tricks med funktioner, der lækker information for at komme forbi en sikkerhedsbarrierevisning.

Hvorfor kan du ikke opdatere security_barrier visninger

Simple visninger i PostgreSQL 9.3 kan automatisk opdateres, men security_barrier synspunkter betragtes ikke som "simple". Det skyldes, at opdatering af visninger er afhængig af at være i stand til at flade visningsunderforespørgslen væk, hvilket gør opdateringen til en simpel opdatering på en tabel. Hele pointen med sikkerhedsbarriere visninger er at forebygge den udfladning. OPDATERING kan i øjeblikket ikke operere direkte på en underforespørgsel, så PostgreSQL vil afvise ethvert forsøg på at opdatere en sikkerhedsbarriere se:

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.

Det er denne begrænsning, jeg er interesseret i at løfte som en del af arbejdet med at fremme sikkerheden på rækkeniveau for AXLE-projektet. Kohei KaiGai har gjort noget fantastisk arbejde med sikkerhed på rækkeniveau og funktioner som security_barrier og lækagesikret er stort set opstået ud af hans arbejde med at tilføje rækkeniveausikkerhed til PostgreSQL. Den næste udfordring er, hvordan man håndterer opdateringer på en sikkerhedsbarriere sikkert og på en måde, der kan vedligeholdes i fremtiden.

Hvorfor underforespørgsler?

Du undrer dig måske over, hvorfor vi skal bruge underforespørgsler til dette. Jeg gjorde. Den korte version er, at vi ikke behøver det, men hvis vi ikke bruger underforespørgsler, må vi i stedet oprette nye ordrefølsomme varianter af AND og ELLER operatører og lære optimizeren, at den ikke kan flytte forholdene hen over dem. Da visninger allerede er udvidet som underforespørgsler, er det meget mindre kompliceret blot at markere underforespørgsler som hegn, der blokerer pull-up/push-down.

Der er dog allerede en kortslutningsordret operation i PostgreSQL – CASE . Problemet med at bruge CASE at nej operationer kan flyttes over grænsen af ​​en CASE , selv LÆKSIK dem. Optimeringsværktøjet kan heller ikke træffe beslutninger om indeksbrug baseret på udtryk inde i en CASE semester. Så hvis vi brugte CASE som jeg spurgte om på -hackere, kunne vi aldrig bruge et indeks til at tilfredsstille en brugerleveret kvalifikation.

I koden

sikkerhedsbarriere support blev tilføjet i 0e4611c0234d89e288a53351f775c59522baed7c . Den blev forbedret med lækagesikker støtte i cd30728fb2ed7c367d545fc14ab850b5fa2a4850 . Krediteringer fremgår af commit-noterne. Tak til alle involverede.

Forsidens funktionsbillede er Security Barrier af Craig A. Rodway, på Flikr

Den forskning, der fører til disse resultater, har modtaget støtte fra EU's syvende rammeprogram (FP7/2007-2013) under tilskudsaftale nr. 318633


  1. VÆLG * FRA flere tabeller. MySQL

  2. ABS() Eksempler i SQL Server

  3. Hvordan kan jeg træde ind i en SQL Server-lagret proc fra min C#-kode?

  4. Flere af mine foretrukne PostgreSQL-forespørgsler - og hvorfor de også betyder noget