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