2016-01-03 21 views
10

sto legando a cogliere il modo migliore per utilizzare la nuova funzionalità di protezione a livello di riga in un database multi-tenant che supporta un'applicazione web.PostgreSQL 9.5 - Row livello di sicurezza/RUOLO migliori pratiche

Attualmente, l'applicazione dispone di un paio di ruoli diversi a disposizione, a seconda dell'azione che sta cercando di prendere.

Una volta che l'applicazione effettua una connessione utilizzando il proprio RUOLO, l'applicazione passa i parametri di autenticazione (forniti dall'utente) in diverse funzioni che filtrano le righe in base ai parametri di autenticazione forniti dall'utente. Il sistema è progettato per funzionare con migliaia di utenti e sembra funzionare; tuttavia, è decisamente goffo (e lento).

Sembra che se volessi utilizzare la nuova funzionalità di sicurezza a livello di riga avrei bisogno di creare un nuovo RUOLO per ciascun utente del mondo reale (non solo per l'applicazione Web) per accedere al database.

È corretto? e se è così, è una buona idea creare migliaia di RUOLI nel database?


Aggiornamento da s' a_horse_with_no_name link nei commenti (grazie, quel filo è impeccabile):

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) 

Ora, sono confuso da current_setting('app_name.app_user') come ero sotto la impressione era solo per i parametri di configurazione ... dove è definito app_name?

+2

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

+0

@a_horse_with_no_name - inchiodato, grazie; comunque, l'esempio dato nel thread è un po 'criptico ... Ho aggiornato la domanda. – losthorse

+0

** è ** per i parametri di "configurazione". Usarli per questo è essenzialmente un "hack". Non è necessario definirli prima di mano - questo può essere fatto dinamicamente. Si noti che 'current_setting ('app_name.app_user')' genererà un errore se il parametro non è stato definito prima. Per evitare ciò, è possibile definire un valore fittizio in 'postgresql.conf' –

risposta

6

definizione delle politiche di sicurezza basate su un ambiente sessione è un male male male idea (io odio entrambi i tappi e grassetto così mi auguro che dico sul serio). Qualsiasi utente puòSET SESSION 'app_name.app_user' = 'bob', quindi non appena qualcuno capisce che "app_name.app_user" è la porta (credetemi, lo faranno), allora tutta la vostra sicurezza è fuori dalla porta.

L'unico modo che vedo è quello di utilizzare una tabella accessibile al webadmin solo che memorizza i token di sessione (uuid tipo viene in mente, per lanciare text per facilità d'uso). La funzione login() è SECURITY DEFINER (supponendo che il proprietario sia webadmin), l'impostazione del token e una sessione SET ting, e quindi la tabella di proprietà (o con privilegi appropriati per) webadmin fa riferimento a tale tabella e all'impostazione di sessione nella sua politica.

Purtroppo, non è possibile utilizzare le tabelle (sessione) temporanei qui perché non si può costruire politiche in una tabella temporanea in modo da avere per utilizzare una tabella "reale". Questo è qualcosa di una penalizzazione delle prestazioni, ma pesano che contro i danni di un hack ...

In pratica:

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; 

E ora la vostra politica punterà alla sessions:

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

(Dal uuid s possono essere assunti per essere unico, senza bisogno di LIMIT 1. ordinamento o di altra magia, se il uuid è nella tabella politica passerà, altrimenti sicuro.) uuid è impossibile da indovinare (entro la tua vita comunque) e impossibile da recuperare da parte di chiunque tranne webadmin.

+0

Questo non è del tutto preciso. I criteri possono essere specificati per un determinato ruolo, quindi, nel tuo esempio, il criterio potrebbe essere creato solo per il ruolo "webadmin" e quindi l'impostazione del GUC "app_name.app_user" avrà un impatto solo sulle query emesse da quel ruolo. Potrebbe essere o non essere sufficiente per i tuoi requisiti di sicurezza, ma impedirebbe a "qualsiasi utente" di accedere a qualsiasi record nella tabella cambiando il GUC. Ciò richiede che l'applicazione che utilizza il ruolo "webadmin" verifichi correttamente l'utente e sia libero da bug di SQL-injection, ovviamente. –

+2

[2 ° quadrante] (http://blog.2ndquadrant.com/application-users-vs-row-level-security/) ha esempi che utilizzano la firma crittografica per gestire l'autorizzazione in un modo che gli utenti non possono ignorare. – spiffytech