sql >> Databasteknik >  >> RDS >> PostgreSQL

Hur fungerar PostgreSQL security_barrier views?

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


  1. Anslut till MySQL på distans

  2. Hur man tar bort nollan när heltalsdelen är noll i Oracle

  3. 4 funktioner som extraherar mikrosekunder från ett tidsvärde i MariaDB

  4. SELECT DISTINCT ignorerar olika fall