Por Adriano Bonacin
Postado em Junho 2016
Revisado por Marcelo Pivovar - Solution Architect
RESUMO
Objetivo deste artigo é explorar uma outra feature do Oracle Database Vault, o Realm. Veremos como é possível impedir o acesso de super users a determinado objeto, role ou mesmo a schemas inteiros.
INTRODUÇÃO
Este é o terceiro de uma série de artigos introdutórios a Oracle Database Vault (ODV). Vimos como configurar, as principais mudanças nas atividades do DBA e a segregação de funções em [1]. Vimos também como funcionam Factor, Rule, Ruleset e Command Rule em [2] e como podemos combiná-los para proteger um objeto, um schema ou mesmo o DB inteiro contra determinada instrução SQL.
Usaremos novamente um DB na versão 12.1.0.2 com o ODV habilitado com as configurações defaults, acrescentando apenas com as mudanças realizadas nos artigos anteriores, mas elas não necessárias. Teremos o C##DONO que possui a role DV_OWNER e o C##CONTAS com a DV_ACCTMGR.
O componente que trataremos neste artigo será o Realm (Domínio) e tentaremos ilustrar sua funcionalidade com alguns exemplos. Ele atua como um nível de proteção “mais forte” que os conhecidos privilégios de sistemas/objetos. O escopo pode ser específico a nível de determinado objeto ou amplo como tipos de objeto ou schemas.
CENÁRIO PARA TESTE
Para preparar nossos testes, vamos começar com a criação de users e roles que serão necessários. Contextualizando também o cenário inicial, vemos que é possível com o SYS consultar todos os objetos do schema HR mesmo com o ODV habilitado.
SYS@pdb1> SELECT VALUE FROM V$OPTION WHERE PARAMETER = 'Oracle Database Vault';
VALUE -------- TRUE
SYS@pdb1> select count(1) from hr.employees;
COUNT(1) ---------- 107
Vamos criar um user USER_CONSULTA_HR que possuirá permissão de leitura em HR.EMPLOYEES e HR.DEPARTMENTS.
C##CONTAS@pdb1> create user USER_CONSULTA_HR identified by oracle; User created.
C##CONTAS@pdb1> grant create session to USER_CONSULTA_HR; Grant succeeded.
HR@pdb1> grant read on employees to USER_CONSULTA_HR; Grant succeeded.
HR@pdb1> grant read on departments to USER_CONSULTA_HR; Grant succeeded.
USER_CONSULTA_HR@pdb1> select count(1) from hr.employees;
COUNT(1) ---------- 107
USER_CONSULTA_HR@pdb1> select count(1) from hr.departments;
COUNT(1) ---------- 27
Criaremos também uma Role que utilizaremos no último exemplo:
SYS@pdb1> create role ROLE_READ_SCOTT; Role created.
REALM
O Realm é como uma zona de proteção em que você pode colocar roles, objetos ou mesmo schemas inteiros. Com ele, é possível blindar os dados contra usuários com privilégios de sistema (como SELECT ANY TABLE, por exemplo). E isso inclui, especialmente, proteção contra o DBA [3].
Adicionar o schema HR dentro de um Realm, por exemplo, provocará erros de falta de privilégios caso seus objetos sejam acessados por um DBA ou alguém que possua SELECT ANY TABLE.
Os atributos de um Realm são:
- Name
- Description
- Enable
- Autid Option
- Type
Os dois primeiros são triviais, olhemos então para o Enable. Mas este também é trivial! Sim, só gostaríamos de chamar a atenção de como devemos utilizá-lo aplicando a API disponibilizada. Os valores aceitos para Enable são estes abaixo:
- DBMS_MACUTL.G_YES (default)
- DBMS_MACUTL.G_NO
Podemos também definir quando auditaremos os acessos. As opções são simplesmente não auditar, auditar falhas, auditar sucessos ou ambos.
Audit_options:
- DBMS_MACUTL.G_REALM_AUDIT_OFF (default)
- DBMS_MACUTL.G_REALM_AUDIT_FAIL
- DBMS_MACUTL.G_REALM_AUDIT_SUCCESS
- DBMS_MACUTL.G_REALM_AUDIT_FAIL + DBMS_MACUTL.G_REALM_AUDIT_SUCCESS
Quanto ao tipo, a discussão é interessante e tentaremos mostrar em detalhes através de exemplos. Há dois tipos: o REGULAR (0) e o MANDATORY (1).
A diferença entre começa no fato de que o REGULAR protege apenas os objetos contra users com “privilégios de sistema” (SELECT ANY TABLE). Porém, isso não impede de usuários que tenham privilégio de objetos de realizar a consulta (grant de SELECT/READ explícito nos objetos do HR ou via ROLE).
Já o Realm MANDATORY é um pouco mais restritivo. Nem mesmo os usuários com privilégio de objeto poderão consultá-los. Neste caso, é necessário conceder “privilégio no Realm” ao usuário que pretende fazer o acesso. Vale frisar que esta discussão inclui o owner dos objetos protegidos. Ou seja, se protegermos o HR com um Realm MANDATORY, ele perde a permissão de consultar os próprios objetos. Interessante, não?
Vejamos como funciona primeiramente os Realms do tipo RELUGAR.
C##DONO@pdb1> BEGIN DBMS_MACADM.CREATE_REALM( realm_name => 'PROTECT_HR', description => 'Proteger HR', enabled => DBMS_MACUTL.G_YES, audit_options => DBMS_MACUTL.G_REALM_AUDIT_FAIL, realm_type => 0); -- 0: Regular END; / PL/SQL procedure successfully completed.
Aqui temos um Realm, entretanto, ele ainda não protege nada. Precisamos dizer quais são os objetos que ele deve englobar. Podemos especificar owner, nome do objeto ou mesmo seu tipo e caso precise generalizar, pode ser utilizado o “wildcard”: ‘%’. Vamos começar adicionando a tabela HR.EMPLOYEES abaixo dele.
C##DONO@pdb1> BEGIN DBMS_MACADM.ADD_OBJECT_TO_REALM(realm_name => 'PROTECT_HR', object_owner => 'HR', object_name => 'EMPLOYEES', object_type => 'TABLE'); END; /
A partir de agora, usuários com privilégios de sistema não conseguirão mais ver os dados desta tabela. Veja o que ocorre com o SYS:
SYS@pdb1> select count(1) from hr.employees; select count(1) from hr.employees * ERROR at line 1: ORA-01031: insufficient privileges
Porém, aqui ainda é possível o acesso de users que possuam privilégios do objeto, como é o caso do USER_CONSULTA_HR:
USER_CONSULTA_HR@pdb1> select count(1) from hr.employees;
COUNT(1) ---------- 107
Antes de avançar, vamos ver como estão as configurações e aproveitaremos para conhecer as views com os metadados de Realms:
C##DONO@pdb1> desc dvsys.dba_dv_realm
Name Null? Type ----------------------------------------- -------- ---------------------------- NAME NOT NULL VARCHAR2(128) DESCRIPTION VARCHAR2(1024) AUDIT_OPTIONS NOT NULL NUMBER REALM_TYPE VARCHAR2(9) ENABLED NOT NULL VARCHAR2(1) C##DONO@pdb1> desc dvsys.dba_dv_realm_object
Name Null? Type ----------------------------------------- -------- ---------------------------- REALM_NAME VARCHAR2(128) OWNER VARCHAR2(128) OBJECT_NAME VARCHAR2(128) OBJECT_TYPE VARCHAR2(32)
Até aqui nenhum segredo, falamos de todos estes atributos. Vejamos como está nosso ambiente.
C##DONO@pdb1> SELECT r.name, r.audit_options, r.realm_type, r.enabled, ro.owner, ro.object_name, ro.object_type FROM dvsys.dba_dv_realm r LEFT OUTER JOIN dvsys.dba_dv_realm_object ro ON r.name = ro.realm_name WHERE r.name = 'PROTECT_HR'; NAME AUDIT_OPTIONS REALM_TYPE E OWNER OBJECT_NAM OBJECT_TYPE ------------ ------------- ------------ - ------------ ---------- ------------ PROTECT_HR 1 REGULAR Y HR EMPLOYEES TABLE
Note que tivemos um outro efeito com esta proteção: não é mais possível conceder permissão de leitura nesta tabela. Resolveremos este ponto adiante.
HR@pdb1> grant read on hr.employees to system; grant read on hr.employees to system * ERROR at line 1: ORA-47401: Realm violation for GRANT on HR.EMPLOYEES
Explorando um pouco mais o conceito de Realm, vamos falar do tipo que é mais restritivo, o MANDATORY. Como citado acima, ele impede quem tenha privilégios de sistema, privilégios de objetos e até mesmo o owner de fazer consultas em objetos protegidos.
Para ilustrar, vamos alterar o tipo do Realm PROTECT_HR para MANDATORY e, com isso, esperamos que o USER_CONSULTA_HR perca o acesso de leitura, visto que será necessário um privilégio a mais para conseguir “chegar” aos dados [4].
C##DONO@pdb1> BEGIN DBMS_MACADM.UPDATE_REALM( realm_name => 'PROTECT_HR', description => 'Proteger HR', enabled => DBMS_MACUTL.G_YES, realm_type => 1); -- 1: MANDATORY END; / PL/SQL procedure successfully completed.
Consultando novamente, vemos que temos um Realm MANDATORY:
C##DONO@pdb1> SELECT r.name, r.audit_options, r.realm_type, r.enabled, ro.owner, ro.object_name, ro.object_type
FROM dvsys.dba_dv_realm r LEFT OUTER JOIN dvsys.dba_dv_realm_object ro ON r.name = ro.realm_name WHERE r.name = 'PROTECT_HR';
NAME AUDIT_OPTIONS REALM_TYPE E OWNER OBJECT_NAM OBJECT_TYPE ------------ ------------- ------------ - ------------ ---------- ------------ PROTECT_HR 1 MANDATORY Y HR EMPLOYEES TABLE
Vamos testar o acesso de USER_CONSULTA_HR aos dados protegidos (EMPLOYEES) e não protegidos (DEPARTMENTS).
USER_CONSULTA_HR@pdb1> select count(1) from hr.employees;
select count(1) from hr.employees * ERROR at line 1: ORA-01031: insufficient privileges USER_CONSULTA_HR@pdb1> select count(1) from hr.departments;
COUNT(1) ---------- 27
Veja o que ocorreu com o HR:
HR@pdb1> select count(1) from hr.employees; select count(1) from hr.employees * ERROR at line 1: ORA-01031: insufficient privileges
O próprio owner perdeu acesso a seus dados. É nesse sentido que afirmamos que o MANDATORY é mais restritivo. Segundo a documentação, com sua utilização é necessária autorização explícita ao Realm. Ou seja, para acessar os dados, além dos devidos privilégios de objeto, será necessária autorização no Realm.
O que seria esta autorização no Realm? São dois os tipos de permissão: Participante (DBMS_MACUTL.G_REALM_AUTH_PARTICIPANT) ou Owner (DBMS_MACUTL.G_REALM_AUTH_OWNER). A principal diferença no tipo de autorização é que o tipo PARTICIPANT não permite a concessão de privilégios sobre os objetos protegidos. Para isso, é necessário a autorização do tipo OWNER.
Autorizando o USER_CONSULTA_HR como PARTICIPANT, resolverá o problema da falta de privilégio. Veja:
C##DONO@pdb1> BEGIN DBMS_MACADM.ADD_AUTH_TO_REALM(realm_name => 'PROTECT_HR', grantee => 'USER_CONSULTA_HR', auth_options => DBMS_MACUTL.G_REALM_AUTH_PARTICIPANT); END; /
Precisamos também aumentar nossa query (removemos alguns campos para simplificar) que verifica a configuração atual, adicionando outra view:
C##DONO@pdb1> desc dvsys.dba_dv_realm_auth
Name Null? Type ----------------------------------------- -------- ---------------------------- REALM_NAME VARCHAR2(128) GRANTEE NOT NULL VARCHAR2(128) AUTH_RULE_SET_NAME VARCHAR2(128) AUTH_OPTIONS VARCHAR2(4000)
C##DONO@pdb1> SELECT r.name, r.realm_type, ro.owner, ro.object_name, ro.object_type, ra.grantee, ra.auth_options
FROM dvsys.dba_dv_realm r LEFT OUTER JOIN dvsys.dba_dv_realm_object ro ON r.name = ro.realm_name LEFT OUTER JOIN dvsys.dba_dv_realm_auth ra ON r.name = ra.realm_name WHERE r.name = 'PROTECT_HR';
NAME REALM_TYPE OWNER OBJECT_NAME OBJECT_TYPE GRANTEE AUTH_OPTIONS ----------- ---------- ------- ------------ ------------ ------------------ ------------ PROTECT_HR MANDATORY HR EMPLOYEES TABLE USER_CONSULTA_HR Participant
E agora podemos ver que USER_CONSULTA_HR é PARTICIPANT do Realm PROTECT_HR. Será que isso resolve o problema de falta privilégio? Resolvido.
USER_CONSULTA_HR@pdb1> select count(1) from hr.employees;
COUNT(1) ---------- 107
Para ilustrar a diferença, vamos conceder o mesmo nível de privilégio para o HR e em seguida vamos resolver um outro ponto citado acima.
C##DONO@pdb1> BEGIN DBMS_MACADM.ADD_AUTH_TO_REALM(realm_name => 'PROTECT_HR', grantee => 'HR', auth_options => DBMS_MACUTL.G_REALM_AUTH_PARTICIPANT); END; / PL/SQL procedure successfully completed.
HR@pdb1> select count(1) from hr.employees;
COUNT(1) ---------- 107
O problema citado foi o user HR não conseguir conceder privilégio de leitura em uma tabela protegida, embora consiga agora acessar a tabela.
HR@pdb1> grant read on hr.employees to system; grant read on hr.employees to system * ERROR at line 1: ORA-47401: Realm violation for GRANT on HR.EMPLOYEES
Para que seja possível, precisamos alterar o tipo de privilégio do HR para OWNER do Realm.
C##DONO@pdb1> BEGIN DBMS_MACADM.UPDATE_REALM_AUTH(realm_name => 'PROTECT_HR', grantee => 'HR', rule_set_name => NULL, auth_options => DBMS_MACUTL.G_REALM_AUTH_OWNER); END; / PL/SQL procedure successfully completed.
Agora temos:
C##DONO@pdb1> SELECT r.name, r.realm_type, ro.owner, ro.object_name, ro.object_type, ra.grantee, ra.auth_options
FROM dvsys.dba_dv_realm r LEFT OUTER JOIN dvsys.dba_dv_realm_object ro ON r.name = ro.realm_name LEFT OUTER JOIN dvsys.dba_dv_realm_auth ra ON r.name = ra.realm_name WHERE r.name = 'PROTECT_HR';
NAME REALM_TYPE OWNER OBJECT_NAME OBJECT_TYPE GRANTEE AUTH_OPTIONS ----------- ---------- ------- ------------ ------------ ------------------ ------------ PROTECT_HR MANDATORY HR EMPLOYEES TABLE HR Owner PROTECT_HR MANDATORY HR EMPLOYEES TABLE USER_CONSULTA_HR Participant
Isto resolve nosso problema, confira:
C##DONO@pdb1> connect hr/hr@pdb1 Connected. HR@pdb1> grant read on hr.employees to system; Grant succeeded.
Faltou ainda falar de um outro atributo da “autorização” a nível de Realm, os Rulesets. Deseja permitir o acesso apenas se determinadas condições forem verificadas? A partir de determinado host? Apenas em certos horários? Este é o ponto chave para atingir este objetivo. Falamos de Rulesets em [http://www.oracle.com/technetwork/pt/articles/idm/rule-ruleset-command-rule-vault-3038750-ptb.html] e citamos que elas poderiam ser utilizadas aqui.
Aumentando a especificidade do nosso exemplo, aproveitarei para incluir um questionamento que nosso time recebeu da equipe de Sec. Como fazer para que a concessão de privilégios tenha prazo de validade? Com os pontos que já discutimos nos artigos anteriores, temos condições de resolver este ponto.
Faremos agora com que o Realm só permita acesso do USER_CONSULTA_HR aos dados até um horário determinado: 10h 35min do dia 18/06/2016. É uma forma não depender de uma ação manual para removê-lo no futuro. Vale enfatizar que o Ruleset está vinculado com a autorização e, portanto, pode afetar apenas determinado user.
Vamos criar uma Rule e um Ruleset e associá-los.
C##DONO@pdb1> BEGIN DBMS_MACADM.CREATE_RULE(rule_name => 'Prazo Acesso HR', rule_expr => 'SYSDATE < TO_DATE(''18/06/2016 10:35:00'',''dd/mm/yyyy hh24:mi:ss'')'); END; / PL/SQL procedure successfully completed.
C##DONO@pdb1> BEGIN DBMS_MACADM.CREATE_RULE_SET(rule_set_name => 'RS_ACESSO_HR', description => 'Ruleset que verifica condicoes para acesso aos dados de HR', enabled => DBMS_MACUTL.G_YES, eval_options => DBMS_MACUTL.G_RULESET_EVAL_ANY, audit_options => DBMS_MACUTL.G_RULESET_AUDIT_FAIL, fail_options => DBMS_MACUTL.G_RULESET_FAIL_SHOW, fail_message => 'Nao eh possivel acessar os dados de HR', fail_code => -20001, handler_options => DBMS_MACUTL.g_ruleset_audit_off, handler => NULL, is_static => FALSE); END; / PL/SQL procedure successfully completed.
C##DONO@pdb1> BEGIN DBMS_MACADM.ADD_RULE_TO_RULE_SET(rule_set_name => 'RS_ACESSO_HR', rule_name => 'Prazo Acesso HR', rule_order => 1, enabled => DBMS_MACUTL.G_YES); END; / PL/SQL procedure successfully completed.
Precisamos também alterar a autorização dada ao USER_CONSULTA_HR para que seja também validado o Ruleset:
C##DONO@pdb1> BEGIN DBMS_MACADM.UPDATE_REALM_AUTH(realm_name => 'PROTECT_HR', grantee => 'USER_CONSULTA_HR', rule_set_name => 'RS_ACESSO_HR', auth_options => DBMS_MACUTL.g_realm_auth_participant); END; / PL/SQL procedure successfully completed.
Verificando a configuração, temos:
C##DONO@pdb1> SELECT r.name, r.realm_type, ro.owner, ro.object_name, ra.grantee, ra.auth_options, ra.auth_rule_set_name
FROM dvsys.dba_dv_realm r LEFT OUTER JOIN dvsys.dba_dv_realm_object ro ON r.name = ro.realm_name LEFT OUTER JOIN dvsys.dba_dv_realm_auth ra ON r.name = ra.realm_name WHERE r.name = 'PROTECT_HR';
NAME REALM_TYPE OWNER OBJECT_NAME GRANTEE AUTH_OPTIONS AUTH_RULE_SE ----------- ----------- ------ ----------- ----------------- ------------- ------------ PROTECT_HR MANDATORY HR EMPLOYEES HR Owner PROTECT_HR MANDATORY HR EMPLOYEES USER_CONSULTA_HR Participant RS_ACESSO_HR
E finalmente, só admirar o resultado:
USER_CONSULTA_HR@pdb1> select count(1), sysdate from hr.employees;
COUNT(1) SYSDATE --------- ------------------- 107 18/06/2016 10:34:58
USER_CONSULTA_HR@pdb1> select count(1), sysdate from hr.employees; select count(1), sysdate from hr.employees * ERROR at line 1: ORA-47306: 20001: Nao eh possivel acessar os dados de HR
Último ponto que discutiremos nesta introdução a Realms, será como proteger também as Roles. No começo do artigo criamos uma role ROLE_READ_SCOTT. Vamos conceder privilégio de leitura em uma tabela do SCOTT e em seguida criar um Realm para proteger a Role. Depois, tentaremos conceder outro privilégio.
SCOTT@pdb1> grant read on emp to ROLE_READ_SCOTT; Grant succeeded. Grant concedido com sucesso. Agora a criação do Realm e incluindo a Role na proteção. C##DONO@pdb1> BEGIN DBMS_MACADM.CREATE_REALM( realm_name => 'PROTECT_ROLE_SCOTT', description => 'Proteger a role ROLE_READ_SCOTT', enabled => DBMS_MACUTL.G_YES, audit_options => DBMS_MACUTL.G_REALM_AUDIT_FAIL, realm_type => 1); END; / PL/SQL procedure successfully completed.
C##DONO@pdb1> BEGIN DBMS_MACADM.ADD_OBJECT_TO_REALM(realm_name => 'PROTECT_ROLE_SCOTT', object_owner => '%', object_name => 'ROLE_READ_SCOTT', object_type => 'ROLE'); END; / PL/SQL procedure successfully completed.
Analisando o resultado:
C##DONO@pdb1> SELECT r.name, r.realm_type, ro.owner, ro.object_name, ro.object_type
FROM dvsys.dba_dv_realm r LEFT OUTER JOIN dvsys.dba_dv_realm_object ro ON r.name = ro.realm_name WHERE r.name = 'PROTECT_ROLE_SCOTT';
NAME REALM_TYPE OWNER OBJECT_NAME OBJECT_TYPE -------------------- -------------------- ---------- --------------- ------------ PROTECT_ROLE_SCOTT MANDATORY % ROLE_READ_SCOTT ROLE
E agora que a Role está protegida, podemos conceder privilégio de leitura na SCOTT.DEPT?
SCOTT@pdb1> grant read on dept to ROLE_READ_SCOTT; grant read on dept to ROLE_READ_SCOTT * ERROR at line 1: ORA-47410: Realm violation for GRANT on ROLE_READ_SCOTT
A forma de “resolver” a violação é fazer com que o SCOTT tenha autorização de OWNER no Realm.
C##DONO@pdb1> BEGIN DBMS_MACADM.ADD_AUTH_TO_REALM(realm_name => 'PROTECT_ROLE_SCOTT', grantee => 'SCOTT', auth_options => DBMS_MACUTL.G_REALM_AUTH_OWNER); END; / PL/SQL procedure successfully completed.
C##DONO@pdb1> SELECT r.name, r.realm_type, ro.object_name, ro.object_type obj_type, ra.grantee, ra.auth_options
FROM dvsys.dba_dv_realm r LEFT OUTER JOIN dvsys.dba_dv_realm_object ro ON r.name = ro.realm_name LEFT OUTER JOIN dvsys.dba_dv_realm_auth ra ON r.name = ra.realm_name WHERE r.name = 'PROTECT_ROLE_SCOTT';
NAME REALM_TYPE OBJECT_NAME OBJ_TYPE GRANTEE AUTH_OPTIONS ------------------- ------------- ------------------ --------- ---------- ------------- PROTECT_ROLE_SCOTT MANDATORY ROLE_READ_SCOTT ROLE SCOTT Owner
E o resultado:
SCOTT@pdb1> grant read on dept to ROLE_READ_SCOTT; Grant succeeded.
Desta forma, vimos que podemos evitar que privilégios sejam adicionados ou removidos de determinada Role se a protegermos com Realm. Somente quem for autorizado como OWNER do Realm poderá fazer o Grant/Revoke.
CONCLUSÃO
Neste artigo exploramos uma outra feature poderosa do Oracle Database Vault. Este é o componente responsável por impedir o acesso de usuários privilegiados aos dados sensíveis. Vimos os diferentes tipos de Realms e que podemos escolher quando auditaremos os acessos (ou tentativas). Falamos também o quão específico ou genéricos podemos ser quando o assunto é proteger os dados.
Há ainda pontos a serem explorados nós próximos artigos como autorização para datapump e patches, auditoria, roles, schedulers, etc. Faremos na medida do possível.
REFERÊNCIA
[1] http://www.oracle.com/technetwork/pt/articles/idm/seguranca-oracle-database-vault-2999070-ptb.html
[2] http://www.oracle.com/technetwork/pt/articles/idm/rule-ruleset-command-rule-vault-3038750-ptb.html
[3] https://docs.oracle.com/database/121/DVADM/cfrealms.htm#DVADM70144
[4] https://docs.oracle.com/database/121/DVADM/cfrealms.htm#DVADM71156
Adriano Bonacin, graduado e mestre em física, é especialista em banco de dados Oracle com foco em alta disponibilidade (RAC, Dataguard, GoldenGate, etc), segurança (ODV, TDE, etc), Tuning, Backup & Recovery e administração em geral. Possui certificações OCP DBA e OCP PL/SQL.
Este artigo foi revisto pela equipe de produtos Oracle e está em conformidade com as normas e práticas para o uso de produtos Oracle.