Du kanske har sett stödet lagt till för security_barrier visningar i PostgreSQL 9.2. Jag har tittat på den koden i syfte att lägga till stöd för automatisk uppdatering för dem som en del av det pågående säkerhetsarbetet på radnivå för AXLE-projektet, och jag tänkte ta chansen att förklara hur de fungerar.
Robert har redan förklarat varför de är användbara och vad de skyddar mot. (Det visar sig att det också diskuteras i vad som är nytt i 9.2). Nu vill jag gå in på hur de arbetar och diskuterar hur säkerhetsbarriär vyer interagerar med automatiskt uppdateringsbara vyer.
Normala vyer
En normal enkel vy expanderas på ett makroliknande sätt som en underfråga som sedan vanligtvis optimeras bort genom att dra upp dess predikat och lägga till det i den innehållande frågan. Det kan vara mer meningsfullt med ett exempel. Tabell:
CREATE TABLE t AS SELECT n, 'secret'||n AS secret FROM generate_series(1,20) n;
och visa:
CREATE VIEW t_odd AS SELECT n, secret FROM t WHERE n % 2 = 1;
en fråga som:
SELECT * FROM t_odd WHERE n < 4
är vyexpanderad inuti frågeomskrivaren till en analysträdrepresentation av en fråga som:
SELECT * FROM (SELECT * FROM t WHERE n % 2 = 1) t_odd WHERE n < 4
som optimeraren sedan plattar ut till en fråga genom att eliminera underfrågan och lägga till WHERE klausultermer till den yttre frågan, som producerar:
SELECT * FROM t t_odd WHERE (n % 2 = 1) AND (n < 4)
Även om du inte kan se de mellanliggande frågorna direkt och de aldrig existerar som riktig SQL, kan du observera denna process genom att aktivera debug_print_parse =on , debug_print_rewritten =på och debug_print_plan =på i postgresql.conf . Jag kommer inte att reproducera analys- och planträden här eftersom de är ganska stora och lätta att generera baserat på exemplen ovan.
Problemet med att använda vyer för säkerhet
Du kanske tror att om du beviljar någon åtkomst till vyn utan att ge dem åtkomst till den underliggande tabellen skulle de inte se jämna rader. Till en början ser det ut som att det är sant:
regress=> SELECT * FROM t_odd WHERE n < 4; n | secret ---+--------- 1 | secret1 3 | secret3 (2 rows)
men när du tittar på planen kanske du ser ett potentiellt 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)
Vy-underfrågan har optimerats bort, med vyns kvalificerare bifogade direkt till den yttre frågan.
I SQL, AND och ELLER är inte beställda. Optimeraren/executorn är fri att köra vilken gren de tror är mer sannolikt att ge dem ett snabbt svar och eventuellt låta dem undvika att köra de andra grenarna. Så om planeraren tycker att n <4 är mycket snabbare än n % 2 =1 det kommer att utvärdera det först. Verkar harmlöst, eller hur? Försök:
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)
Hoppsan! Som du kan se ansågs den av användaren tillhandahållna predikatfunktionen vara billigare att köra än de andra testerna, så den klarades varje rad innan vyns predikat hade uteslutit det. En skadlig funktion kan använda samma knep för att kopiera raden.
säkerhetsbarriär visningar
säkerhetsbarriär vyer fixar det genom att tvinga kvalificeringarna på vyn att köras först, innan några kvalificeringar som tillhandahålls av användaren körs. Istället för att utöka vyn och lägga till eventuella vykvalificerare till den yttre frågan, ersätter de referensen till vyn med en underfråga. Den här underfrågan har säkerhetsbarriären flaggan inställd på dess intervall-tabellpost, som talar om för optimeraren att den inte ska platta till underfrågan eller trycka ner yttre frågevillkor i den som den skulle göra för en normal underfråga.
Så med en säkerhetsbarriärvy:
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)
Frågeplanen bör berätta vad som händer, även om den inte visar säkerhetsbarriärattributet i förklara-utgången. Den kapslade underfrågan tvingar fram en skanning på t med vykvalificeraren körs den av användaren tillhandahållna funktionen på resultatet av underfrågan.
Men. Vänta en sekund. Varför är det av användaren tillhandahållna predikatet n <4 även inuti underfrågan? Är inte det ett potentiellt säkerhetshål? Om n <4 är nedtryckt, varför är inte f_leak(hemlig) ?
LÄCKSÄKER operatörer och funktioner
Förklaringen till det är att < operatören är märkt med LEAKPROOF . Det här attributet indikerar att en operatör eller funktion är betrodd att inte läcka information, så den kan säkert tryckas ner genom security_barrier vyer. Av uppenbara skäl kan du inte ställa in LEAKPROOF som en vanlig användare:
regress=> ALTER FUNCTION f_leak(text) LEAKPROOF; ERROR: only superuser can define a leakproof function
och superanvändaren kan redan göra vad de vill, så de behöver inte tillgripa tricks med funktioner som läcker information för att komma förbi en säkerhetsbarriärvy.
Varför kan du inte uppdatera security_barrier visningar
Enkla vyer i PostgreSQL 9.3 kan uppdateras automatiskt, men security_barrier åsikter anses inte vara "enkla". Det beror på att uppdatering av vyer förlitar sig på att kunna platta bort vyunderfrågan, vilket gör uppdateringen till en enkel uppdatering på en tabell. Hela poängen med säkerhetsbarriär visningar är att förebygga den där förplattan. UPPDATERA kan för närvarande inte använda en underfråga direkt, så PostgreSQL kommer att avvisa alla försök att uppdatera en säkerhetsbarriär visa:
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 är denna begränsning som jag är intresserad av att lyfta som en del av arbetet med att utveckla säkerheten på radnivå för AXLE-projektet. Kohei KaiGai har gjort ett fantastiskt arbete med säkerhet på radnivå och funktioner som security_barrier och LÄCKSÄKER har till stor del uppstått ur hans arbete med att lägga till säkerhet på radnivå till PostgreSQL. Nästa utmaning är hur man hanterar uppdateringar av en säkerhetsbarriär på ett säkert sätt och på ett sätt som kommer att kunna underhållas i framtiden.
Varför underfrågor?
Du kanske undrar varför vi måste använda underfrågor för detta. Jag gjorde. Den korta versionen är att vi inte måste, men om vi inte använder undersökningar måste vi istället skapa nya orderkänsliga varianter av OCH och ELLER operatörer och lär optimeraren att den inte kan flytta förhållanden över dem. Eftersom vyer redan har utökats som underfrågor är det mycket mindre komplicerat att bara flagga underfrågor som stängsel som blockerar pull-up/push-down.
Det finns dock redan en kortslutande beställd operation i PostgreSQL – CASE . Problemet med att använda CASE att nej operationer kan flyttas över gränsen för ett CASE , även LÄCKSÄTT ettor. Inte heller kan optimeraren fatta beslut om indexanvändning baserat på uttryck i ett CASE termin. Så om vi använde CASE som jag frågade om på -hackers kunde vi aldrig använda ett index för att tillfredsställa en användarlevererad kvalificering.
I koden
säkerhetsbarriär stöd lades till i 0e4611c0234d89e288a53351f775c59522baed7c . Den förbättrades med läckagesäkert stöd i cd30728fb2ed7c367d545fc14ab850b5fa2a4850 . Krediter visas i commit-anteckningarna. Tack till alla inblandade.
Framsidans funktionsbild är Security Barrier av Craig A. Rodway, på Flikr
Den forskning som leder till dessa resultat har fått finansiering från Europeiska unionens sjunde ramprogram (FP7/2007-2013) under bidragsavtal nr 318633