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