VPD - Virtual Private Database - Segurança dos dados em nível de linha

 

Em um artigo anterior nós fizemos uma breve introdução à segurança em nível de linha (Row Level Security).

 

Neste artigo vamos falar sobre o VPD - Virtual Private Database - uma feature que a Oracle disponibiliza nas versões Enterprise do seus bancos de dados, a partir da versão 8i, para implementar  a segurança em nível de linha.

 

 

Necessidade

 

A segurança dos dados contidos em um Banco de Dados, até bem pouco tempo, era possível ser garantida simplesmente através dos privilégios de objeto, controlando as operações DML básicas (select, insert, update e delete). No entanto, nos dias de hoje, com o mundo conectado através da internet, ganham cada vez mais espaço as web applications (sistemas desenvolvidos para a web).

 

A necessidade de disponibilidade de dados on-line e informações em tempo real impulsionam o desenvolvimento deste tipo de aplicação.

 

Um banco de dados voltado para ambiente web possui características e necessidades peculiares como, por exemplo, a convergência de dados de múltiplos bancos para um único database, fazendo com que dados de diferentes organizações sejam consolidados em um mesmo objeto do banco.

 

Nos bancos voltados para web, os privilégios de objeto, por si só, não são suficientes para controlar o acesso e garantir a segurança dos dados. Isso porque não será possível apenas dizer que um determinado usuário tem permissão de leitura em uma determinada tabela, mas sim, a quais dados desta tabela ele deve ter acesso. Se não fosse assim, este usuário teria acesso a informações de várias entidades.

 

Isso é segurança de dados em nível de linha (Row Level Security).

 

É possível implementar e controlar row level security por meio das aplicações, mas o desenvolvimento e a manutenção das políticas são trabalhosas e não trazem um bom nível de confiabilidade, pois somente o acesso por meio destas aplicações é que estaria assegurado. Se um usuário conseguisse acessar o banco por outra aplicação ou ferramenta (SQL Plus, SQL Developer, Toad, etc), ele não estaria sujeito à política de segurança e poderia ter acesso a todos os dados de um determinado objeto.

 

 

VPD – Virtual Private Database

 

A Oracle possibilita a implementação de row level security por meio do VPD, uma feature nativa da versão Enterprise (8i em diante).

 

A idéia chave do VPD é adicionar uma condição extra na cláusula WHERE de todos os DMLs, de forma transparente ao usuário.

 

Nos DMLs concebidos pelo usuário sem uma cláusula WHERE, o VPD se encarrega de 'criar' uma, com os predicados definidos pela política de segurança. Esta política pode ser anexada a tabelas, views e sinônimos e é acionada quando os DMLs acessam os objetos associados com a política.

 

A segurança dos dados passa então a ser centralizada no banco de dados, valendo para todo e qualquer acesso ao banco, independente da fonte (aplicação, ferramenta de terceiros, SQL*Plus, etc.).

 

 

Application Context

 

Application contexts (contextos de aplicação) são úteis para determinar como a política será implementada, e poderão ser usados na procedure responsável em definir o predicado, a condição 'extra' que será anexada às cláusulas WHERE.

 

Os contextos podem ser alimentados por meio de trigger (de logon, por exemplo) ou a partir de uma chamada em um determinado ponto da aplicação. Estes contextos podem conter informações sobre conexão (IP, host, usuário) ou conteúdo de qualquer coluna de tabela do banco (nome, código ou cargo do usuário, código da entidade, centro de custo, departamento, etc), o que melhor definir a coluna alvo do VPD, que será usada para definir os privilégios de acesso.

 

 

Exemplo:

Consideremos a existência de duas tabelas: gerentes e salarios, conforme abaixo:

 

gerentes

cod_pessoa nome             cod_empresa

              1            Maria                              1

              2            João                                2

              3            Pedro                              3

 

 

salarios

funcionario      salario        cod_empresa

José                   1000                         1

Antônio               2000                         3

Tereza                3000                         1

Betânia               4000                         2

 

 

Imaginemos que a coluna alvo do VPD seja a cod_empresa, e esta informação é passada para um contexto no momento em que o usuário faz o logon no banco (por meio de logon trigger).

 

Neste caso, quando a gerente Maria fizer o logon, a trigger chama a procedure que alimenta o contexto e este recebe o valor 1, que é o código do setor da gerente Maria.

 

Suponhamos ainda que a política de segurança tenha sido adicionada à tabela salarios, e no momento em que a gerente Maria fizer um SELECT para saber os salários dos funcionários, o VPD vai se encarregar de anexar o predicado 'cod_empresa=1' a todas as cláusulas WHERE.

 

Então, o SELECT da gerente Maria passa a retornar somente os funcionários José e Tereza, que pertentem à empresa 1.

 

SELECT da gerente Maria:

 

         select * from salarios;

 

Neste momento o VPD iria alterar o comando para:

 

         select * from salarios where cod_empresa=1;

 

E teríamos como resultado:

 

funcionario   cod_setor     salario

José                1        1000

Tereza            1        3000

 

 

Passo a passo

 

Passo 1 - Criação do application context

 

create context ctx_emps using proc_ctx_emp;

 

Passo 2 -Package pra setar o valor do contexto

 

create or replace package proc_ctx_emp

is

procedure set_ctx_emp(retorno varchar2);

end;

 

create or replace package body proc_ctx_emp

  is

    procedure set_ctx_emp (retorno varchar2) is

    v_ctx_emp varchar2(100);

  begin

    dbms_session.set_context('ctx_emps','ctx_emp',retorno);

  end proc_ctx_emp;

 

 

Passo 3 - Package pra setar o conteúdo do predicado (cláusula where)

 

create or replace package proc_ctx_vpd_emp

is

function fc_emp(d1 varchar2, d2 varchar2) return varchar2;

end;

 

create or replace package body proc_ctx_vpd_emp is

function fc_emp (d1 varchar2, d2 varchar2) return varchar2 is

  d_predicate varchar2(2000);

begin

    d_predicate := '1=2';

-- não aplicar a política ao usuário DBA, para que ele possa ver todos os dados das tabelas

    IF (SYS_CONTEXT('USERENV','SESSION_USER') = 'DBA') THEN

      d_predicate := NULL;

    ELSE

-- adicionar a condição cod_empresa = <conteúdo do contexto> a todas as cláusulas WHERE

      d_predicate := 'cod_empresa=sys_context(''ctx_emps'',''ctx_emp'')';

    END IF;

    RETURN d_predicate;

end fc_emp;

end proc_ctx_vpd_emp;

 

 

Passo 4 - Procedure para adicionar a política às tabelas que possuem a coluna  cod_empresa

 

begin

   for x in (select table_name

              from user_tab_cols

              where column_name='cod_empresa')

   loop

      SYS.DBMS_RLS.ADD_POLICY ( object_schema    =>  'usuario_owner_das_tabelas',

                                      object_name      =>  x.table_name,

                                      policy_name      =>  'vpd_emp',

                                      function_schema  =>  'usuario_owner_das_tabelas',

                                      policy_function  =>  'proc_ctx_vpd_emp.fc_emp',

                                      statement_types  =>  'SELECT,INSERT,UPDATE,DELETE',

                                      update_check     =>  TRUE,

                                      enable           =>  TRUE );

   end loop;

end;

/

 

Passo 5 - Criar trigger para que seja executada a procedure que alimenta o contexto a cada logon

 

create or replace trigger log_on_vpd

after logon on database

begin

call proc_ctx_emp.set_ctx_emp

end;

 

Pronto!

Com isso, após cada usuário logar no banco, o VPD irá identificar o seu cod_empresa e fará com que todos os selects, inserts, updates e deletes filtrem pelo seu cod_empresa.

 

PS.: Há particularidades referentes aos inserts, onde não é naturalmente usada uma cláusula WHERE, e que não vamos abordar neste artigo, mas pode ser usado o contexto como valor default da coluna alvo do vpd.

 

 

Considerações finais

 

Esta feature disponibilizada pela Oracle atende às necessidades das web applications, onde os dados de várias entidades passam a compartilhar os mesmos objetos de banco.

 

E o melhor: de forma nativa, implementada pelo próprio fabricante do banco de dados .

 

Podemos destacar os seguintes pontos positivos:

 

-         O uso de VPD reduz a possibilidade de falhas de segurança que existem em políticas implementadas somente nas aplicações, assegurando todos os acessos independente da fonte;

-         Facilita a manutenção;

-         Diminui o tempo de implementação;

-         Possui bom desempenho, por se tratar de uma feature nativa;

-         É transparente ao usuário, entre várias outras vantagens.

 

Existem outras features que a Oracle disponibiliza que podem ser implementadas juntamente com o VPD, como por exemplo grupos de políticas, colunas relevantes e máscara para as colunas alvo do VPD (Relevant Column and Masking) e rótulos de segurança (LABEL SECURITY).

 

Estas outras features também estão documentadas nos manuais da Oracle e podem ser abordadas em futuros artigos.

 

 

Anderson Rodrigo Farias

DBA Oracle

Betha Sistemas LTDA

http://www.betha.com.br