PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Como funcionam as visualizações security_barrier do PostgreSQL?


Você pode ter visto o suporte adicionado para security_barrier visualizações no PostgreSQL 9.2. Estive analisando esse código com o objetivo de adicionar suporte de atualização automática para eles como parte do progresso do trabalho de segurança em nível de linha para o projeto AXLE e pensei em aproveitar a oportunidade para explicar como eles funcionam.

Robert já explicou por que eles são úteis e contra o que protegem. (Acontece que também é discutido no que há de novo em 9.2). Agora quero entrar em como eles trabalham e discutem como security_barrier views interagem com views automaticamente atualizáveis.

Visualizações normais


Uma visão simples normal é expandida de maneira semelhante a macro como uma subconsulta que geralmente é otimizada puxando seu predicado para cima e anexando-o aos quals da consulta que a contém. Isso pode fazer mais sentido com um exemplo.Tabela dada:
CREATE TABLE t AS SELECT n, 'secret'||n AS secret FROM generate_series(1,20) n;

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

uma consulta como:
SELECT * FROM t_odd WHERE n < 4

é expandido para visualização dentro do reescritor de consulta em uma representação de árvore de análise de uma consulta como:
SELECT * FROM (SELECT * FROM t WHERE n % 2 = 1) t_odd WHERE n < 4

que o otimizador então nivela em uma consulta de passagem única, eliminando a subconsulta e anexando o WHERE termos da cláusula para a consulta externa, produzindo:
SELECT * FROM t t_odd WHERE (n % 2 = 1) AND (n < 4)

Mesmo que você não possa ver as consultas intermediárias diretamente e elas nunca existam como SQL real, você pode observar esse processo ativando debug_print_parse =on , debug_print_rewritten =ativado e debug_print_plan =ativado em postgresql.conf . Não vou reproduzir as árvores de análise e planejamento aqui, pois elas são muito grandes e fáceis de gerar com base nos exemplos acima.

O problema de usar visualizações para segurança


Você pode pensar que conceder a alguém acesso à exibição sem conceder acesso à tabela subjacente impediria que eles vissem as linhas de número par. Inicialmente parece que é verdade:
regress=> SELECT * FROM t_odd WHERE n < 4;
 n | secret  
---+---------
 1 | secret1
 3 | secret3
(2 rows)

mas quando você olha para o plano, pode ver um problema em potencial:
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)

A subconsulta de exibição foi otimizada, com os qualificadores da exibição anexados diretamente à consulta externa.

Em SQL, E e OU não são ordenados. O otimizador/executor é livre para executar qualquer ramificação que achar mais provável de fornecer uma resposta rápida e possivelmente evitar a execução de outras ramificações. Então, se o planejador achar que n <4 é muito mais rápido que n % 2 =1 ele vai avaliar isso primeiro. Parece inofensivo, certo? Experimentar:
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)

Opa! Como você pode ver, a função de predicado fornecida pelo usuário foi considerada mais barata de executar do que os outros testes, portanto, foi aprovada em todas as linhas antes que o predicado da visualização a excluísse. Uma função maliciosa pode usar o mesmo truque para copiar a linha.

barreira_de segurança visualizações


security_barrier views corrigem isso forçando os qualificadores na visão a serem executados primeiro, antes que qualquer qualificador fornecido pelo usuário seja executado. Em vez de expandir a exibição e anexar qualquer qualificador de exibição à consulta externa, eles substituem a referência à exibição por uma subconsulta. Esta subconsulta tem a security_barrier sinalizador definido em sua entrada da tabela de intervalo, que informa ao otimizador que não deve nivelar a subconsulta ou enviar condições de consulta externa para ela, como faria para uma subconsulta normal.

Assim, com uma visão de barreira de segurança:
CREATE VIEW t_odd_sb WITH (security_barrier) AS SELECT n, secret FROM t WHERE n % 2 = 1;

Nós temos:
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)

O plano de consulta deve informar o que está acontecendo, embora não mostre o atributo de barreira de segurança na saída de explicação. A subconsulta aninhada força uma varredura em t com o qualificador de visualização, a função fornecida pelo usuário é executada no resultado da subconsulta.

Mas. Espere um segundo. Por que o predicado fornecido pelo usuário é n <4 também dentro da subconsulta? Isso não é uma possível falha de segurança? Se n <4 é pressionado, por que não é f_leak(secret) ?

À PROVA DE FUGA operadores e funções


A explicação para isso é que o < operador está marcado como LEAKPROOF . Este atributo indica que um operador ou função é confiável para não vazar informações, portanto, pode ser enviado com segurança por meio de security_barrier Visualizações. Por motivos óbvios, você não pode definir LEAKPROOF como um usuário comum:
regress=> ALTER FUNCTION f_leak(text)  LEAKPROOF;
ERROR:  only superuser can define a leakproof function

e o superusuário já pode fazer o que quiser, então não precisa recorrer a truques com funções vazando informações para passar por uma visão de barreira de segurança.

Por que você não pode atualizar security_barrier visualizações


Visualizações simples no PostgreSQL 9.3 são automaticamente atualizáveis, mas security_barrier visualizações não são consideradas “simples”. Isso ocorre porque a atualização de visualizações depende da capacidade de nivelar a subconsulta de visualização, transformando a atualização em uma atualização simples em uma tabela. O ponto principal da security_barrier visualizações é prevenir aquele achatamento. ATUALIZAÇÃO atualmente não pode operar diretamente em uma subconsulta, então o PostgreSQL rejeitará qualquer tentativa de atualizar uma security_barrier visualizar:
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.

É essa limitação que estou interessado em levantar como parte do trabalho para progredir na segurança em nível de linha para o projeto AXLE. Kohei KaiGai fez um ótimo trabalho com segurança em nível de linha e recursos como security_barrier e À PROVA DE FUGA surgiram em grande parte de seu trabalho para adicionar segurança em nível de linha ao PostgreSQL. O próximo desafio é como lidar com atualizações em uma barreira de segurança com segurança e de forma que seja sustentável no futuro.

Por que subconsultas?


Você pode estar se perguntando por que temos que usar subconsultas para isso. Eu fiz. A versão curta é que não precisamos, mas se não usarmos subconsultas, teremos que criar novas variantes sensíveis à ordem do AND e OU operadores e ensinar ao otimizador que ele não pode mover condições entre eles. Como as visualizações já são expandidas como subconsultas, é muito menos complicado apenas sinalizar subconsultas como cercas que bloqueiam pull-up/push-down.

Já existe uma operação ordenada de curto-circuito no PostgreSQL – CASE . O problema de usar CASE que não as operações podem ser movidas através do limite de um CASE , até mesmo À PROVA DE FUGA uns. O otimizador também não pode tomar decisões de uso de índice com base em expressões dentro de um CASE prazo. Então, se usarmos CASE como perguntei sobre -hackers, nunca poderíamos usar um índice para satisfazer um qualificador fornecido pelo usuário.

No código


security_barrier suporte foi adicionado em 0e4611c0234d89e288a53351f775c59522baed7c . Foi aprimorado com suporte à prova de vazamentos em cd30728fb2ed7c367d545fc14ab850b5fa2a4850 . Os créditos aparecem nas notas de confirmação. Obrigado a todos os envolvidos.

A imagem da primeira página é Security Barrier por Craig A. Rodway, no Flikr

A pesquisa que levou a esses resultados recebeu financiamento do Sétimo Programa-Quadro da União Europeia (FP7/2007-2013) sob o contrato de doação n° 318633