sql >> Database >  >> RDS >> PostgreSQL

Hoe werken PostgreSQL security_barrier views?

Je hebt misschien de ondersteuning gezien die is toegevoegd voor security_barrier weergaven in PostgreSQL 9.2. Ik heb die code onderzocht met het oog op het toevoegen van automatische update-ondersteuning voor hen als onderdeel van het voortschrijdende beveiligingswerk op rijniveau voor het AXLE-project, en ik dacht dat ik de kans zou nemen om uit te leggen hoe ze werken.

Robert heeft al uitgelegd waarom ze nuttig zijn en waartegen ze beschermen. (Het blijkt dat het ook wordt besproken in wat er nieuw is in 9.2). Nu wil ik ingaan op hoe ze werken en bespreken hoe security_barrier weergaven werken samen met automatisch bij te werken weergaven.

Normale weergaven

Een normale eenvoudige weergave wordt op een macro-achtige manier uitgebreid als een subquery die dan meestal wordt geoptimaliseerd door het predikaat omhoog te trekken en toe te voegen aan de quals van de bevattende query. Dat is misschien logischer met een voorbeeld. Gegeven tabel:

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

en bekijk:

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

een zoekopdracht als:

SELECT * FROM t_odd WHERE n < 4

wordt in de query-rewriter uitgevouwen tot een ontledingsboomweergave van een query zoals:

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

die de optimizer vervolgens afvlakt tot een single-pass-query door de subquery te elimineren en de WHERE toe te voegen clausuletermen naar de buitenste query, waardoor:

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

Ook al kun je de tussenliggende queries niet direct zien en bestaan ​​ze nooit als echte SQL, je kunt dit proces wel observeren door debug_print_parse =on in te schakelen. , debug_print_rewritten =op en debug_print_plan =op in postgresql.conf . Ik zal de parseer- en planbomen hier niet reproduceren, omdat ze vrij groot zijn en gemakkelijk te genereren op basis van de bovenstaande voorbeelden.

Het probleem met het gebruik van weergaven voor beveiliging

Je zou kunnen denken dat als je iemand toegang verleent tot de weergave zonder hen toegang te verlenen tot de onderliggende tabel, ze zouden stoppen met het zien van even genummerde rijen. In eerste instantie lijkt dat waar te zijn:

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

maar als u naar het plan kijkt, ziet u mogelijk een mogelijk probleem:

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)

De weergave-subquery is verder geoptimaliseerd, waarbij de kwalificaties van de weergave rechtstreeks aan de buitenste query zijn toegevoegd.

In SQL, EN en OF zijn niet besteld. Het staat de optimizer/uitvoerder vrij om elke branch uit te voeren waarvan zij denken dat het hen een snel antwoord geeft en mogelijk laat ze voorkomen dat ze de andere branches draaien. Dus als de planner denkt dat n <4 is veel sneller dan n % 2 =1 het zal dat eerst evalueren. Lijkt ongevaarlijk, toch? Probeer:

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)

Oeps! Zoals u kunt zien, werd de door de gebruiker geleverde predikaatfunctie beschouwd als goedkoper om uit te voeren dan de andere tests, dus werd deze elke rij doorstaan ​​voordat het predikaat van de weergave deze had uitgesloten. Een kwaadwillende functie zou dezelfde truc kunnen gebruiken om de rij te kopiëren.

security_barrier weergaven

security_barrier views lossen dit op door de kwalificaties op de view te forceren om eerst te worden uitgevoerd, voordat door de gebruiker geleverde kwalificaties worden uitgevoerd. In plaats van de weergave uit te breiden en weergavekwalificaties toe te voegen aan de buitenste query, vervangen ze de verwijzing naar de weergave door een subquery. Deze subquery heeft de security_barrier vlag ingesteld op zijn bereik-tabelinvoer, die de optimizer vertelt dat hij de subquery niet moet afvlakken of buitenste queryvoorwaarden erin moet duwen zoals bij een normale subquery.

Dus met een zicht op de veiligheidsbarrière:

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

we krijgen:

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)

Het queryplan moet u vertellen wat er gebeurt, hoewel het het kenmerk van de beveiligingsbarrière niet in de uitvoer van uitleg laat zien. De geneste subquery dwingt een scan af op t met de view qualifier, dan wordt de door de gebruiker geleverde functie uitgevoerd op het resultaat van de subquery.

Maar. Wacht eens even. Waarom is het door de gebruiker geleverde predikaat n <4 ook binnen de subquery? Is dat niet een potentieel beveiligingslek? Als n <4 naar beneden is gedrukt, waarom is f_leak(secret) . niet ?

LEKBESTENDIG operators en functies

De verklaring daarvoor is dat de < operator is gemarkeerd met LEAKPROOF . Dit kenmerk geeft aan dat een operator of functie vertrouwd is om geen informatie te lekken, zodat deze veilig door security_barrier kan worden geduwd keer bekeken. Om voor de hand liggende redenen kun je LEAKPROOF . niet instellen als gewone gebruiker:

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

en de superuser kan al doen wat ze willen, dus ze hoeven geen trucjes uit te halen met functies die informatie lekken om voorbij een beveiligingsbarrière te komen.

Waarom kun je security_barrier niet updaten weergaven

Eenvoudige weergaven in PostgreSQL 9.3 kunnen automatisch worden bijgewerkt, maar security_barrier weergaven worden niet als "eenvoudig" beschouwd. Dat komt omdat het bijwerken van weergaven afhankelijk is van het kunnen afvlakken van de weergave-subquery, waardoor de update een eenvoudige update op een tabel wordt. Het hele punt van security_barrier weergaven is om voorkomen dat afvlakken. UPDATE kan momenteel niet rechtstreeks op een subquery werken, dus PostgreSQL zal elke poging om een ​​security_barrier bij te werken afwijzen bekijken:

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.

Het is deze beperking die ik wil opheffen als onderdeel van het werk om de beveiliging op rijniveau voor het AXLE-project te verbeteren. Kohei KaiGai heeft geweldig werk verricht met beveiliging op rijniveau en functies zoals security_barrier en LEKBESTENDIG zijn grotendeels voortgekomen uit zijn werk om beveiliging op rijniveau toe te voegen aan PostgreSQL. De volgende uitdaging is hoe u veilig en op een manier kunt omgaan met updates op een beveiligingsbarrière die in de toekomst onderhouden kunnen worden.

Waarom subquery's?

Je vraagt ​​je misschien af ​​waarom we hiervoor subquery's moeten gebruiken. Ik deed. De korte versie is dat we dat niet hoeven te doen, maar als we geen subquery's gebruiken, moeten we in plaats daarvan nieuwe ordergevoelige varianten maken van de AND en OF operators en leer de optimizer dat het geen voorwaarden over hen kan verplaatsen. Aangezien weergaven al zijn uitgevouwen als subquery's, is het veel minder ingewikkeld om subquery's te markeren als hekken die pull-up / push-down blokkeren.

Er is echter al een opdracht voor kortsluiting in PostgreSQL - CASE . Het probleem met het gebruik van CASE dat nee bewerkingen kunnen worden verplaatst over de grens van een CASE , zelfs LEKBESTENDIG degenen. De optimizer kan ook geen beslissingen nemen over het gebruik van indexen op basis van expressies in een CASE termijn. Dus als we CASE . gebruiken zoals ik op -hackers vroeg, zouden we nooit een index kunnen gebruiken om te voldoen aan een door de gebruiker geleverde kwalificatie.

In de code

security_barrier ondersteuning is toegevoegd in 0e4611c0234d89e288a53351f775c59522baed7c . Het is verbeterd met lekvrije ondersteuning in cd30728fb2ed7c367d545fc14ab850b5fa2a4850 . Credits verschijnen in de vastleggingsnotities. Dank aan alle betrokkenen.

Afbeelding van de functie op de voorpagina is Security Barrier door Craig A. Rodway, op Flikr

Het onderzoek dat tot deze resultaten heeft geleid, heeft financiering ontvangen van het zevende kaderprogramma van de Europese Unie (FP7/2007-2013) onder subsidieovereenkomst nr. 318633


  1. Verzamel schemastatistieken met FND_STATS in EBS 11i en R12

  2. De standaardtaal instellen voor alle nieuwe aanmeldingen in SQL Server (T-SQL)

  3. Beperking wijzigen?

  4. stel een leeg wachtwoord in voor PostgreSQL-gebruiker