PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Wie funktionieren postgreSQL security_barrier-Ansichten?

Sie haben vielleicht gesehen, dass die Unterstützung für security_barrier hinzugefügt wurde Ansichten in PostgreSQL 9.2. Ich habe mir diesen Code angesehen, um im Rahmen der fortschreitenden Sicherheitsarbeit auf Zeilenebene für das AXLE-Projekt Unterstützung für automatische Updates hinzuzufügen, und ich dachte, ich würde die Gelegenheit nutzen, um zu erklären, wie sie funktionieren.

Robert hat bereits erklärt, warum sie nützlich sind und wovor sie schützen. (Es stellt sich heraus, dass es auch in den Neuerungen in 9.2 besprochen wird). Jetzt möchte ich auf das Wie eingehen sie arbeiten und diskutieren, wie security_barrier Ansichten interagieren mit automatisch aktualisierbaren Ansichten.

Normale Aufrufe

Eine normale einfache Ansicht wird makroartig als Unterabfrage erweitert, die dann normalerweise wegoptimiert wird, indem ihr Prädikat nach oben gezogen und an die Quals der enthaltenden Abfrage angehängt wird. Das könnte mit einem Beispiel sinnvoller sein. Gegebene Tabelle:

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

und anzeigen:

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

eine Abfrage wie:

SELECT * FROM t_odd WHERE n < 4

wird innerhalb des Abfrageumschreibers in eine Analysebaumdarstellung einer Abfrage erweitert, wie z. B.:

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

die der Optimierer dann zu einer Single-Pass-Abfrage zusammenfasst, indem er die Unterabfrage eliminiert und das WHERE anhängt Klauselausdrücke in die äußere Abfrage und erzeugen:

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

Auch wenn Sie die Zwischenabfragen nicht direkt sehen können und sie nie als echtes SQL existieren, können Sie diesen Vorgang beobachten, indem Sie debug_print_parse =on aktivieren , debug_print_rewritten =on und debug_print_plan =on in postgresql.conf . Ich werde die Parsing- und Plan-Bäume hier nicht reproduzieren, da sie ziemlich groß und basierend auf den obigen Beispielen einfach zu generieren sind.

Das Problem mit der Verwendung von Ansichten für die Sicherheit

Sie könnten denken, jemandem Zugriff auf die Ansicht zu gewähren, ohne ihm Zugriff auf die zugrunde liegende Tabelle zu gewähren, würde verhindern, dass er Zeilen mit geraden Zahlen sieht. Zunächst sieht es so aus, als ob das stimmt:

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

aber wenn Sie sich den Plan ansehen, sehen Sie möglicherweise ein potenzielles 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)

Die Ansichts-Unterabfrage wurde wegoptimiert, wobei die Qualifizierer der Ansicht direkt an die äußere Abfrage angehängt wurden.

In SQL UND und ODER sind nicht bestellt. Es steht dem Optimierer/Ausführer frei, den Zweig auszuführen, von dem er glaubt, dass er ihm mit größerer Wahrscheinlichkeit eine schnelle Antwort gibt, und ihn möglicherweise vermeiden lässt, die anderen Zweige auszuführen. Wenn also der Planer denkt, dass n <4 ist viel schneller als n % 2 =1 es wird das zuerst auswerten. Scheint harmlos, oder? Versuchen Sie:

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)

Hoppla! Wie Sie sehen können, wurde die vom Benutzer bereitgestellte Prädikatfunktion als billiger in der Ausführung angesehen als die anderen Tests, sodass jede Zeile bestanden wurde, bevor das Prädikat der Ansicht sie ausgeschlossen hatte. Eine bösartige Funktion könnte den gleichen Trick anwenden, um die Zeile zu kopieren.

Sicherheitsbarriere Aufrufe

Sicherheitsbarriere Ansichten beheben dies, indem sie erzwingen, dass die Qualifizierer für die Ansicht zuerst ausgeführt werden, bevor vom Benutzer bereitgestellte Qualifizierer ausgeführt werden. Anstatt die Ansicht zu erweitern und alle Ansichtsqualifizierer an die äußere Abfrage anzuhängen, ersetzen sie den Verweis auf die Ansicht durch eine Unterabfrage. Diese Unterabfrage hat die security_barrier Flag, das für seinen Bereichstabelleneintrag gesetzt ist, was dem Optimierer mitteilt, dass er die Unterabfrage nicht glätten oder äußere Abfragebedingungen in sie hineinschieben sollte, wie es bei einer normalen Unterabfrage der Fall wäre.

Also mit Blick auf die Sicherheitsbarriere:

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

wir bekommen:

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)

Der Abfrageplan sollte Ihnen mitteilen, was passiert, obwohl er das Sicherheitsbarrierenattribut in der EXPLAIN-Ausgabe nicht anzeigt. Die verschachtelte Unterabfrage erzwingt einen Scan auf t mit dem Sichtbezeichner, dann wird die vom Benutzer bereitgestellte Funktion auf dem Ergebnis der Unterabfrage ausgeführt.

Aber. Moment mal. Warum ist das vom Benutzer bereitgestellte Prädikat n <4 auch innerhalb der Unterabfrage? Ist das nicht eine potenzielle Sicherheitslücke? Wenn n <4 nach unten gedrückt wird, warum nicht f_leak(secret) ?

AUSLAUFSICHER Operatoren und Funktionen

Die Erklärung dafür ist, dass < Operator ist mit LEAKPROOF gekennzeichnet . Dieses Attribut gibt an, dass einem Operator oder einer Funktion vertraut wird, keine Informationen preiszugeben, sodass sie sicher durch die security_barrier gedrückt werden können Ansichten. Aus offensichtlichen Gründen können Sie LEAKPROOF nicht einstellen als normaler Benutzer:

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

und der Superuser kann bereits tun, was er will, sodass er nicht auf Tricks mit Funktionen zurückgreifen muss, die Informationen preisgeben, um eine Sicherheitsbarrierenansicht zu umgehen.

Warum können Sie security_barrier nicht aktualisieren Aufrufe

Einfache Ansichten in PostgreSQL 9.3 sind automatisch aktualisierbar, aber security_barrier Ansichten gelten nicht als „einfach“. Das liegt daran, dass das Aktualisieren von Ansichten davon abhängt, dass die Ansichtsunterabfrage abgeflacht werden kann, wodurch die Aktualisierung in eine einfache Aktualisierung einer Tabelle umgewandelt wird. Der ganze Sinn von security_barrier Aufrufe ist zu verhindern dieses Abflachen. AKTUALISIEREN kann derzeit nicht direkt mit einer Unterabfrage arbeiten, daher wird PostgreSQL jeden Versuch ablehnen, eine security_barrier zu aktualisieren Ansicht:

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.

Es ist diese Einschränkung, die ich im Rahmen der Arbeit zur Verbesserung der Sicherheit auf Zeilenebene für das AXLE-Projekt aufheben möchte. Kohei KaiGai hat großartige Arbeit mit Sicherheit auf Zeilenebene und Funktionen wie security_barrier geleistet und LECKSICHER sind größtenteils aus seiner Arbeit hervorgegangen, PostgreSQL Sicherheit auf Zeilenebene hinzuzufügen. Die nächste Herausforderung besteht darin, mit Updates einer Sicherheitsbarriere sicher und zukunftsfähig umzugehen.

Warum Unterabfragen?

Sie fragen sich vielleicht, warum wir dafür Unterabfragen verwenden müssen. Ich tat. Die Kurzversion ist, dass wir das nicht müssen, aber wenn wir keine Unterabfragen verwenden, müssen wir stattdessen neue reihenfolgeabhängige Varianten des AND erstellen und ODER Operatoren und bringen Sie dem Optimierer bei, dass er Bedingungen nicht über sie hinweg verschieben kann. Da Ansichten bereits als Unterabfragen erweitert sind, ist es viel einfacher, Unterabfragen einfach als Zäune zu kennzeichnen, die Pull-Up/Push-Down blockieren.

In PostgreSQL gibt es jedoch bereits eine kurzschließende geordnete Operation – CASE . Das Problem bei der Verwendung von CASE das nein Operationen können über die Grenze eines CASE verschoben werden , sogar LECKSICHER Einsen. Der Optimierer kann auch keine Indexverwendungsentscheidungen basierend auf Ausdrücken innerhalb eines CASE treffen Begriff. Wenn wir also CASE verwenden Wie ich nach on -hackers gefragt habe, könnten wir niemals einen Index verwenden, um einen vom Benutzer bereitgestellten Qualifizierer zu erfüllen.

Im Code

Sicherheitsbarriere Unterstützung wurde in 0e4611c0234d89e288a53351f775c59522baed7c hinzugefügt . Es wurde mit Leakproof-Unterstützung in cd30728fb2ed7c367d545fc14ab850b5fa2a4850 erweitert . Credits erscheinen in den Commit-Notizen. Danke an alle Beteiligten.

Titelbild auf der Titelseite ist Security Barrier von Craig A. Rodway auf Flikr

Die zu diesen Ergebnissen führende Forschung wurde vom Siebten Rahmenprogramm der Europäischen Union (RP7/2007-2013) unter der Finanzhilfevereinbarung Nr. 318633 finanziert