39.5. Regras e privilégios #

Devido à reescrita de consultas pelo sistema de regras do PostgreSQL, outras tabelas/visões além daquelas usadas na consulta original são acessadas. Quando são usadas regras de atualização, isto pode incluir acesso de escrita às tabelas.

As regras de reescrita não têm um dono separado. O dono da relação (tabela ou visão) é automaticamente o dono das regras de reescrita definidas para ela. O sistema de regras do PostgreSQL altera o comportamento do sistema de controle de acesso padrão. Com exceção das regras SELECT associadas às visões do invocador de segurança (veja CREATE VIEW, todas as relações utilizadas devido a regras são verificadas em relação aos privilégios do dono da regra, e não do usuário que a invoca. Isto significa que, com exceção das visões do invocador de segurança, os usuários precisam apenas dos privilégios necessários para as tabelas/visões que são explicitamente indicadas em suas consultas.

Por exemplo: um usuário possui uma lista de telefones onde alguns são privados, outros são de interesse do assistente do escritório. O usuário pode construir assim:

CREATE TABLE phone_data (person text, phone text, private boolean);
CREATE VIEW phone_number AS
    SELECT person, CASE WHEN NOT private THEN phone END AS phone
    FROM phone_data;
GRANT SELECT ON phone_number TO assistant;

Ninguém, exceto aquele usuário (e os superusuários do banco de dados), pode acessar a tabela phone_data. Mas devido ao GRANT, o assistente pode executar o SELECT na visão phone_number. O sistema de regras irá reescrever o SELECT em phone_number em um SELECT em phone_data. Como o usuário é o dono de phone_number e, portanto, o dono da regra, o acesso de leitura a phone_data agora é verificado em relação aos privilégios do usuário, e a consulta é permitida. A verificação de acesso a phone_number também é realizada, mas isto é feito com relação ao usuário chamador, portanto, ninguém além do usuário e do assistente pode usar a visão.

Os privilégios são verificados regra por regra. Portanto, o assistente é, por enquanto, o único que pode ver os números de telefone públicos. Mas o assistente pode configurar outra visão e conceder acesso à mesma ao público. Então, qualquer um poderá ver os dados de phone_number através da visão do assistente. O que o assistente não pode fazer é criar uma visão que acesse diretamente phone_data. (Na verdade, o assistente pode, mas não irá funcionar, porque todos os acessos serão negados durante as verificações de permissão.) E assim que o usuário perceber que o assistente abriu sua visão phone_number, o usuário poderá revogar o acesso do assistente. Imediatamente, qualquer acesso à visão do assistente falharia.

Pode-se pensar que esta verificação regra por regra é uma falha de segurança, mas, na verdade, não é. Mas se não funcionasse assim, o assistente poderia definir uma tabela com as mesmas colunas de phone_number, e copiar os dados para lá uma vez por dia. Então, são os dados do próprio assistente, e o assistente pode conceder acesso a todos que desejar. Um comando GRANT significa Eu confio em você. Se alguém em que se confia fizer algo assim, é hora de pensar sobre isto e então usar o comando REVOKE.

Note que enquanto as visões podem ser usadas para ocultar o conteúdo de certas colunas usando a técnica mostrada acima, elas não podem ser usadas para ocultar de forma confiável os dados em linhas invisíveis, a menos que tenha sido definido o sinalizador security_barrier. Por exemplo, a visão a seguir é insegura:

CREATE VIEW phone_number AS
    SELECT person, phone FROM phone_data WHERE phone NOT LIKE '412%';

Esta visão pode parecer segura, já que o sistema de regras irá reescrever qualquer SELECT de phone_number em SELECT de phone_data, e adicionar a qualificação de que somente se deseja as entradas onde phone não começa com 412. Mas se o usuário puder criar suas próprias funções, não será difícil convencer o planejador a executar a função definida pelo usuário antes da expressão NOT LIKE. Por exemplo:

CREATE FUNCTION tricky(text, text) RETURNS bool AS $$
BEGIN
    RAISE NOTICE '% => %', $1, $2;
    RETURN true;
END;
$$ LANGUAGE plpgsql COST 0.0000000000000000000001;

SELECT * FROM phone_number WHERE tricky(person, phone);

Cada pessoa e número de telefone na tabela phone_data será mostrada como um NOTICE, porque o planejador escolherá executar a função tricky barata antes da função NOT LIKE mais cara. Mesmo que o usuário seja impedido de definir novas funções, podem ser usadas as funções internas em ataques semelhantes. (Por exemplo, a maioria das funções de conversão inclui seus valores de entrada nas mensagens de erro que produzem.)

Considerações semelhantes se aplicam às regras de atualização. Nos exemplos da seção anterior, o dono das tabelas no banco de dados de exemplo pode conceder os privilégios SELECT, INSERT, UPDATE, e DELETE, na visão cadarço para outra pessoa, mas apenas SELECT na tabela cadarço_log. A ação da regra para escrever entradas de registro ainda será executada com êxito, e este outro usuário poderá ver as entradas de registro. Mas não poderia criar entradas falsas, nem manipular ou remover as existentes. Neste caso, não há possibilidade de subverter as regras convencendo o planejador a alterar a ordem das operações, porque a única regra que referencia cadarço_log é um INSERT não qualificado. Isto pode não ser verdade em cenários mais complexos.

Quando for necessário que uma visão forneça segurança no nível de linha, o atributo security_barrier deverá ser aplicado à visão. Isto impede que funções e operadores escolhidos de forma maliciosa recebam valores de linhas até que a visão tenha feito seu trabalho. Por exemplo, se a visão mostrada acima tivesse sido criada da seguinte forma, ela seria segura:

CREATE VIEW phone_number WITH (security_barrier) AS
    SELECT person, phone FROM phone_data WHERE phone NOT LIKE '412%';

As visões criadas com a security_barrier podem ter um desempenho muito pior do que as visões criadas sem esta opção. Em geral, não há como evitar isto: o plano mais rápido possível deve ser rejeitado se puder comprometer a segurança. Por este motivo, esta opção não é ativada por padrão.

O planejador de consultas tem mais flexibilidade ao lidar com funções que não possuem efeitos colaterais. Estas funções são referidas como LEAKPROOF, e incluem muitos operadores simples e comumente usados, como muitos operadores de igualdade. O planejador de consulta pode permitir com segurança que estas funções sejam avaliadas em qualquer ponto do processo de execução da consulta, porque chamá-las em linhas invisíveis para o usuário não irá vazar nenhuma informação sobre as linhas não vistas. Além disso, as funções que não recebem argumentos, ou que não recebem nenhum argumento da visão de barreira de segurança, não precisam ser marcadas como LEAKPROOF para serem empurradas para baixo, porque nunca recebem dados da visão. Em contraste, uma função que pode relatar um erro dependendo dos valores recebidos como argumentos (como uma que relata um erro em caso de estouro ou divisão por zero) não é à prova de vazamentos, podendo fornecer informações significativas sobre as linhas não vistas se aplicada antes dos filtros de linha da visão de segurança.

Por exemplo, uma varredura de índice não pode ser selecionada para consultas em visões de barreira de segurança (ou em tabelas com políticas de segurança no nível de linha) se um operador usado na cláusula WHERE estiver associado à família de operador do índice, mas sua função subjacente não estiver marcada como LEAKPROOF. O meta-comando \dAo+ do psql é útil para listar as famílias de operador e determinar quais deles estão marcados como sendo à prova de vazamentos.

É importante entender que até mesmo uma visão criada com a opção security_barrier é destinada a ser segura apenas no sentido limitado de que o conteúdo das tuplas invisíveis não será passado para funções possivelmente inseguras. O usuário pode ter outros meios de fazer inferências sobre os dados não vistos; por exemplo, podem ver o plano de consulta usando EXPLAIN, ou medir o tempo de execução de consultas em relação à visão. Um invasor mal-intencionado pode inferir algo sobre a quantidade de dados não vistos, ou até mesmo obter algumas informações sobre a distribuição dos dados ou valores mais comuns (já que estas coisas podem afetar o tempo de execução do plano; ou ainda, visto que também se refletem nas estatísticas do otimizador, a escolha do plano). Se estes tipos de ataques de "canal encoberto" [122] forem motivo de preocupação, provavelmente não é aconselhável conceder qualquer acesso aos dados.



[122] covert channel: Um canal intra-sistema não intencional ou não autorizado permitindo que duas entidades cooperantes transfiram informações em uma forma que viole a política de segurança do sistema, mas não exceda as autorizações de acesso das entidades.