2016-01-03 13 views
10

Chciałbym uchwycić najlepszy sposób użycia nowej funkcji bezpieczeństwa na poziomie wiersza w bazie danych dla wielu dzierżawców, która obsługuje aplikację internetową.PostgreSQL 9.5 - Bezpieczeństwo na poziomie wiersza/Najlepsze praktyki ROLE

Obecnie aplikacja ma kilka różnych ROLE, w zależności od podejmowanej akcji.

Gdy aplikacja nawiąże połączenie przy użyciu własnej ROLI, aplikacja przekazuje parametry uwierzytelniania (dostarczone przez użytkownika) do różnych funkcji, które odfiltrowują wiersze na podstawie dostarczonych przez użytkownika parametrów uwierzytelniania. System jest przeznaczony do pracy z tysiącami użytkowników i wygląda na to, że działa; jednak jest wyzywająco niezgrabny (i powolny).

Wygląda na to, że gdybym chciał skorzystać z nowej funkcji bezpieczeństwa na poziomie wiersza, musiałbym utworzyć nową ROLĘ dla każdego użytkownika w świecie rzeczywistym (nie tylko dla aplikacji internetowej), aby uzyskać dostęp do bazy danych.

Czy to prawda? a jeśli tak, to czy dobrze jest stworzyć tysiące ROLE w bazie danych?


Aktualizacja z a_horse_with_no_name „s link w komentarzach (dzięki, że gwint jest na miejscu):

CREATE USER application; 

CREATE TABLE t1 (id int primary key, f1 text, app_user text); 
INSERT INTO t1 VALUES(1,'a','bob'); 
INSERT INTO t1 VALUES(2,'b','alice'); 
ALTER TABLE t1 ENABLE ROW LEVEL SECURITY; 
CREATE POLICY P ON t1 USING (app_user = current_setting('app_name.app_user')); 
GRANT SELECT ON t1 TO application; 

SET SESSION AUTHORIZATION application; 

SET app_name.app_user = 'bob'; 

SELECT * FROM t1; 

id | f1 | app_user 
----+----+---------- 
    1 | a | bob 
(1 row) 

SET app_name.app_user = 'alice'; 
SELECT * FROM t1; 

id | f1 | app_user 
----+----+---------- 
    2 | b | alice 
(1 row) 

SET app_name.app_user = 'none'; 
SELECT * FROM t1; 

id | f1 | app_user 
----+----+---------- 
(0 rows) 

Teraz jestem zdezorientowany przez current_setting('app_name.app_user') jak byłem pod wrażenie było tylko dla parametrów konfiguracyjnych ... gdzie zdefiniowano app_name?

+2

http://www.postgresql.org/message-id/[email protected] –

+0

@a_horse_with_no_name - przybity to, dzięki; jednakże przykład podany w wątku jest nieco zagadkowy ... Zaktualizowałem to pytanie. – losthorse

+0

It ** jest ** dla parametrów "konfiguracyjnych". Używanie ich w tym celu jest zasadniczo "hack". Nie musisz ich definiować przed ręką - można to zrobić dynamicznie. Zauważ, że 'current_setting ('nazwa_aplikacji.app_user')' spowoduje błąd, jeśli parametr nie został wcześniej zdefiniowany. Aby temu zapobiec, możesz zdefiniować fałszywą wartość w 'postgresql.conf' –

Odpowiedz

6

firmowych polityk bezpieczeństwa w oparciu o ustawienie sesji jest BAD BAD BAD pomysł (nienawidzę oba dekielki i bold więc uwierzcie mi, że mam na myśli). Każdy użytkownik może SET SESSION 'app_name.app_user' = 'bob', tak szybko, jak ktoś zorientuje się, że "app_name.app_user" jest drzwi (zaufaj mi, będą), a następnie całe zabezpieczenie jest na drzwiach.

Jedynym sposobem, jaki widzę, jest użycie tabeli dostępnej tylko dla twojego webadmin, która przechowuje tokeny sesyjne (typ uuid przychodzi na myśl, rzutowany na text dla łatwości użycia). Funkcja login() to SECURITY DEFINER (zakładając, że właściciel webadmin), ustawiając token, a także sesję SET, a następnie tabela, która jest własnością (lub ma odpowiednie uprawnienia dla) webadmin, odnosi się do tej tabeli i ustawienia sesji w jej polityce.

Niestety, nie można tu użyć tabel tymczasowych (sesji), ponieważ nie można tworzyć reguł dla tabeli tymczasowej, więc trzeba użyć "prawdziwej" tabeli. To jest coś w rodzaju kary wydajności, lecz ważyć że przed uszkodzeniem hack ...

W praktyce:

CREATE FUNCTION login (uname text, pwd text) RETURNS boolean AS $$ 
DECLARE 
    t uuid; 
BEGIN 
    PERFORM * FROM users WHERE user = uname AND password = pwd; 
    IF FOUND THEN 
    INSERT INTO sessions SET token = uuid_generate_v4()::text, user .... 
     RETURNING token INTO t; 
    SET SESSION "app_name.token" = t; 
    RETURN true; 
    ELSE 
    SET SESSION "app_name.token" = ''; 
    RETURN false; 
    END IF; 
END; $$ LANGUAGE plpgsql STRICT; 

A teraz wasza polityka będzie Link do sessions:

CREATE POLICY p ON t1 FOR SELECT 
    USING (SELECT true FROM sessions WHERE token = current_setting('app_name.token')); 

(Ponieważ uuid s może być uważany za unikatowy, nie ma potrzeby zamawiania lub innej magii, jeśli uuid jest w tabeli, to polityka przejdzie, inaczej nie.) uuid jest niemożliwe do odgadnięcia (w twoim życiu i tak) i niemożliwe do odzyskania przez nikogo oprócz webadmin.

+0

To nie jest całkowicie dokładne. Dla danej roli można określić zasady, dlatego w twoim przykładzie zasady można utworzyć tylko dla roli "webadmin", a ustawienie GUC "app_name.app_user" będzie miało wpływ tylko na kwerendy wystawiane przez tę rolę. To może, ale nie musi, wystarczać dla twoich wymagań bezpieczeństwa, ale uniemożliwiłoby "każdemu użytkownikowi" dostęp do dowolnego rekordu w tabeli poprzez zmianę GUC. Wymaga to oczywiście, aby aplikacja używająca roli "webadmin" prawidłowo weryfikowała użytkownika i była wolna od błędów iniekcyjnych SQL. –

+2

[2nd Quadrant] (http://blog.2ndquadrant.com/application-users-vs-row-level-security/) zawiera przykłady użycia podpisu kryptograficznego do zarządzania autoryzacją w sposób, którego użytkownicy nie mogą ominąć. – spiffytech