Protegendo seu Banco de Dados com o SQL Firewall do Oracle 23ai/26ai

Com o lançamento do Oracle Database 23ai, uma das novas funcionalidades de segurança lançadas foi o SQL Firewall. Essa funcionalidade vem como um reforço de segurança atuando como um firewall nativo do banco de dados, para comandos SQL.

Neste post, vou apresentar o que é o SQL Firewall, como configurá-lo e alguns exemplos práticos que mostram como ele funciona na proteção contra SQL injection e comandos não autorizados.

O que é o SQL Firewall?

O SQL Firewall é um recurso de segurança de banco de dados incorporado ao kernel do Oracle Database 23ai, que inspeciona as conexões e instruções SQL recebidas, permitindo, registrando ou bloqueando atividades não autorizadas conforme as políticas específicas de cada usuário do banco de dados. 

O SQL Firewall garante que apenas instruções SQL explicitamente autorizadas sejam executadas, bloqueando tentativas de injeção de SQL.

Ele está disponível apenas para versões Enterprise Edition a partir da 23ai com a option database vault e nas versões cloud abaixo?

  • Oracle Base Database Service Enterprise Edition - High Performance (BaseDB EE-HP)
  • Oracle Base Database Service Enterprise Edition - Extreme Performance (BaseDB EE-EP)
  •  Oracle Exadata Database Service on Dedicated Infrastructure (ExaDB)
  • Oracle Exadata Database Service on Cloud@Customer (ExaDB)

Como funciona?

O SQL Firewall funciona aprendendo o tráfego SQL normal de um usuário do banco de dados, incluindo quais instruções SQL são esperadas desse usuário e o contexto em que ele se conecta ao banco de dados, como o endereço de rede, o usuário do sistema operacional e o programa utilizado. Uma vez treinado, o SQL Firewall pode:

  • Registrar e bloquear desvios do comportamento normal

  • Identificar instruções SQL incomuns

  • Identificar conexões provenientes de endereços ou programas que não estão na lista de permissões

Configurando o SQL Firewall

    1 - Habilitando o SQL Firewall para um usuário

Para habilitar é usada a procedure dbms_sql_firewall.enable. Este processo deve ser executado com usuário que tenha como permissão a role SQL_FIREWALL_ADMIN.

SQL> select status from dba_sql_firewall_status;
STATUS
--------
DISABLED

SQL> exec dbms_sql_firewall.enable;

PL/SQL procedure successfully completed.

SQL> select status from dba_sql_firewall_status;

STATUS
--------
ENABLED

SQL>

    2 - Iniciando o modo de captura

Nesta etapa é iniciado um processo de captura para rastrear todas as consultas de um usuário específico. Para iniciar o processo é usada a procedure dbms_sql_firewall.create_capture.

SQL>
begin
  dbms_sql_firewall.create_capture (
    username       => 'LAMIM',
    top_level_only => true,
    start_capture  => true);
end;
/
SQL>   2    3    4    5    6    7
PL/SQL procedure successfully completed.

SQL>

A partir deste momento as consultas realizadas pelo usuário serão capturadas. Para simular o processo, criei 2 tabelas, inseri alguns dados fictícios e depois realizei um select nas mesmas.

SQL>
SQL> create table LAMIM.TABELA01(id number);

Table created.

SQL> insert into LAMIM.TABELA01 values (1);

1 row created.

SQL> insert into LAMIM.TABELA01 values (2);

1 row created.

SQL> create table LAMIM.TABELA02(data date);

Table created.

SQL> insert into LAMIM.TABELA02 values (sysdate);

1 row created.

insert into LAMIM.TABELA02 values (sysdate +1);

1 row created. 

SQL> commit;

SQL> show user;
USER is "LAMIM"
SQL> select * from tabela01;

        ID
----------
         1
         2

SQL> select * from tabela02;

DATA
---------
13-APR-25
14-APR-25

SQL>

Na view dba_sql_firewall_capture_logs é possível verificar os dados que foram coletados durante o modo captura.

SQL>
SQL> set linesize 210 pagesize 40
SQL> column command_type format a12
SQL> column current_user format a15
column client_program format a45
column os_user format a10
column ip_address format a10
SQL> SQL> SQL> SQL> column sql_text format a90

select command_type,
       current_user,
       client_program,
SQL> SQL>   2    3    4         os_user,
       ip_address,
       sql_text
  5    6    7  from   dba_sql_firewall_capture_logs
where  username = 'LAMIM';  8

COMMAND_TYPE CURRENT_USER    CLIENT_PROGRAM                                OS_USER    IP_ADDRESS SQL_TEXT
------------ --------------- --------------------------------------------- ---------- ---------- ------------------------------------------------------------------------------------------
SELECT       LAMIM           sqlplus@dbsystem23ai (TNS V1-V3)              oracle     10.0.0.32  SELECT * FROM TABELA01
SELECT       LAMIM           sqlplus@dbsystem23ai (TNS V1-V3)              oracle     10.0.0.32  SELECT * FROM TABELA01
INSERT       LAMIM           sqlplus@dbsystem23ai (TNS V1-V3)              oracle     10.0.0.32  INSERT INTO TABELA01 VALUES (:"SYS_B_0")
INSERT       LAMIM           sqlplus@dbsystem23ai (TNS V1-V3)              oracle     10.0.0.32  INSERT INTO TABELA02 VALUES (SYSDATE +:"SYS_B_0")
SELECT       LAMIM           sqlplus@dbsystem23ai (TNS V1-V3)              oracle     10.0.0.32  SELECT * FROM TABELA02
SELECT       LAMIM           sqlplus@dbsystem23ai (TNS V1-V3)              oracle     10.0.0.32  SELECT * FROM TABELA02
SELECT       LAMIM           sqlplus@dbsystem23ai (TNS V1-V3)              oracle     10.0.0.32  SELECT DECODE (USER,:"SYS_B_0",XS_SYS_CONTEXT (:"SYS_B_1",:"SYS_B_2"),USER) FROM SYS.DUAL
SELECT       LAMIM           sqlplus@dbsystem23ai (TNS V1-V3)              oracle     10.0.0.32  SELECT DECODE (USER,:"SYS_B_0",XS_SYS_CONTEXT (:"SYS_B_1",:"SYS_B_2"),USER) FROM SYS.DUAL

8 rows selected.

SQL>

    3 - parando o modo de captura

Nesta etapa é parado o processo de captura para rastrear todas as consultas de um usuário específico. Para parar a captura é usada a procedure dbms_sql_firewall.stop_capture.

Supondo que a captura esteve ativa durante tempo suficiente para coletar o padrão de SQL do usuário, agora é o momento de parar a mesma. 

SQL>
SQL> exec dbms_sql_firewall.stop_capture('LAMIM');

PL/SQL procedure successfully completed.

SQL>

Agora será gerada uma lista de permissões (allow-list) para o usuário com base nos logs de captura. O SQL Firewall cria a lista de permissões com base nos dados coletados dos registros de captura existentes para o usuário.

SQL>
SQL> exec dbms_sql_firewall.generate_allow_list ('LAMIM');

PL/SQL procedure successfully completed.

SQL>

Existem quatro views que nos permitem verificar o conteúdo da lista de permissões (allow-list). São elas a dba_sql_firewall_allowed_ip_addr, dba_sql_firewall_allowed_os_prog, dba_sql_firewall_allowed_os_user e dba_sql_firewall_allowed_sql 

SQL>
SQL> --Consultar o usuario e IP
column username format a30
select *
from   dba_sql_firewall_allowed_ip_addr
where  username = 'LAMIM';
SQL> SQL>   2    3
USERNAME                       IP_ADDRESS
------------------------------ ----------
LAMIM                          10.0.0.32

SQL> --Consultar o usuario e PROGRAMA
column os_program format a60
select *
from   dba_sql_firewall_allowed_os_prog
where  username = 'LAMIM';SQL> SQL>   2    3

USERNAME                       OS_PROGRAM
------------------------------ ------------------------------------------------------------
LAMIM                          sqlplus@dbsystem23ai (TNS V1-V3)

SQL> --Consultar o usuario e usuario de SO
column os_user format a20
select *
from   dba_sql_firewall_allowed_os_user
where  username = 'LAMIM';SQL> SQL>   2    3

USERNAME                       OS_USER
------------------------------ --------------------
LAMIM                          oracle

SQL> --Consultar o usuario e SQL
column sql_text format a90
select current_user,
       sql_text
from   dba_sql_firewall_allowed_sql
where  username = 'LAMIM';
SQL> SQL>   2    3    4
CURRENT_USER    SQL_TEXT
--------------- ------------------------------------------------------------------------------------------
LAMIM           CREATE TABLE TABELA03 AS SELECT * FROM TABELA02
LAMIM           INSERT INTO TABELA02 VALUES (SYSDATE +:"SYS_B_0")
LAMIM           INSERT INTO TABELA01 VALUES (:"SYS_B_0")
LAMIM           SELECT * FROM TABELA01
LAMIM           SELECT * FROM TABELA02
LAMIM           SELECT DECODE (USER,:"SYS_B_0",XS_SYS_CONTEXT (:"SYS_B_1",:"SYS_B_2"),USER) FROM SYS.DUAL

6 rows selected.

SQL>
SQL> 

    3 - Ativando a Política de SQL Firewall para Proteção de Usuários

Nesta etapa é ativada a proteção do usuário. A partir do momento em que está ativa, o SQL Firewall passa a validar as listas de permissões sempre que o usuário se conecta e executa comandos SQL. 

Essa ativação é imediata e se aplica inclusive às sessões que já estiverem em andamento para o usuário em questão.

Para iniciar a proteção é usada a procedure dbms_sql_firewall.enable_allow_list, passando além do usuário, o tipo de bloqueio (enforcement), sendo eles:

ENFORCE_CONTEXT: Aplica apenas a lista de permissões de contexto (endereço IP, usuário do sistema operacional e programa utilizado).

ENFORCE_SQL: Aplica apenas a lista de permissões de instruções SQL.

ENFORCE_ALL: Aplica tanto a lista de permissões de contexto quanto a de instruções SQL.

Neste exemplo, habilitamos tanto a lista de permissões de contexto quanto a de SQL. Definir o parâmetro BLOCK como true significa que as violações serão bloqueadas, já false significa que as violações serão registradas em log, mas não serão bloqueadas.

SQL>
SQL> begin
  dbms_sql_firewall.enable_allow_list (
    username => 'LAMIM',
    enforce  => dbms_sql_firewall.enforce_all,
    block    => false);
end;
/  2    3    4    5    6    7

PL/SQL procedure successfully completed.

SQL>

Exemplo prático: tentativa de SQL Injection

Após o modo ENFORCE estar ativado, se um atacante tentar executar algum comando diferente, ou com um IP diferente, o Oracle irá bloquear automaticamente a execução desse comando, registrando o evento nas views de auditoria.

No exemplo acima a politica esta ativa com block => false, desta forma ele vai permitir a conexão gerar a lista de violações.

SQL> set lines 210
col ip_address for a20
col sql_text for a160
select sql_text,
       firewall_action,
       ip_address,
       cause
from   dba_sql_firewall_violations
where  username = 'LAMIM';SQL> SQL> SQL>   2    3    4    5    6

SQL_TEXT                                                                                                                                                         FIREWAL IP_ADDRESS           CAUSE
---------------------------------------------------------------------------------------------------------------------------------------------------------------- ------- -------------------- -----------------
SELECT * FROM V$VERSION WHERE BANNER LIKE :"SYS_B_0"                                                                                                             Allowed 179.127.190.77       SQL violation
SELECT CON_ID FROM (SELECT SYS_CONTEXT (:"SYS_B_0",:"SYS_B_1") AS CON_ID FROM DUAL) WHERE CON_ID=:"SYS_B_2"                                                      Allowed 179.127.190.77       SQL violation
SELECT INST_COUNT FROM (SELECT COUNT (*) INST_COUNT FROM GV$DATABASE) WHERE INST_COUNT >:"SYS_B_0"                                                               Allowed 179.127.190.77       SQL violation
DECLARE L_SCHEMA VARCHAR2 (?); L_PART1 VARCHAR2 (?); L_PART2 VARCHAR2 (?); L_DBLINK VARCHAR2 (?); L_PART1_TYPE NUMBER; L_OBJID NUMBER; V_NAME VARCHAR2 (?); BEGI Allowed 179.127.190.77       SQL violation
N V_NAME :=:OBJ_NAME; DBMS_UTILITY.NAME_RESOLVE (NAME => V_NAME,CONTEXT => ?,SCHEMA => L_SCHEMA,PART1 => L_PART1,PART2 => L_PART2,DBLINK => L_DBLINK,PART1_TYPE
=> L_PART1_TYPE,OBJECT_NUMBER => L_OBJID); EXCEPTION WHEN OTHERS THEN IF SQLCODE=-? THEN DBMS_UTILITY.NAME_RESOLVE (NAME => V_NAME,CONTEXT => ?,SCHEMA => L_SCHE
MA,PART1 => L_PART1,PART2 => L_PART2,DBLINK => L_DBLINK,PART1_TYPE => L_PART1_TYPE,OBJECT_NUMBER => L_OBJID); ELSE RAISE; END IF; END;

SELECT USER_ID FROM USER_USERS WHERE ALL_SHARD=:"SYS_B_0"                                                                                                        Allowed 179.127.190.77       SQL violation

Alterando o block para true, através da procedure dbms_sql_firewall.update_allow_list_enforcement, as violações passam a ser bloqueadas.

SQL>
SQL>
SQL> begin
  dbms_sql_firewall.update_allow_list_enforcement(
    username => 'LAMIM',
    enforce  => dbms_sql_firewall.enforce_all,
    block    => true);
end;
/  2    3    4    5    6    7

PL/SQL procedure successfully completed.

SQL> select username,
       status,
       top_level_only,
       enforce,
       block
from   dba_sql_firewall_allow_lists
where username='LAMIM';  2    3    4    5    6    7

USERNAME                       STATUS   TOP_LEVEL_ONLY ENFORCE         BLOCK
------------------------------ -------- -------------- --------------- --------------
LAMIM                          ENABLED  Y              ENFORCE_ALL     Y

SQL>

Ao tentar realizar um conexao, partindo de outro IP, recebo o erro abaixo no SQL Developer.

ORA-47605: Violação do SQL Firewall

Da mesma forma, se tentarmos realizar um comando SQL diferente dos que foram coletados durante o período ativo, teremos o mesmo erro.

SQL>
SQL> select count(1) from tabela02;
select count(1) from tabela02
                     *
ERROR at line 1:
ORA-47605: SQL Firewall violation
Help: https://docs.oracle.com/error-help/db/ora-47605/


SQL>

Conclusão

O SQL Firewall do Oracle 23ai é uma adição poderosa para reforçar a segurança de aplicações que acessam o banco de dados. Com uma configuração relativamente simples, ele entrega uma camada de proteção eficaz contra ataques comuns, como SQL Injection. Por ser integrado diretamente com o Kernel do banco de dados a Oracle considera seu overhead em performance quase imperceptivel.

https://www.oracle.com/a/ocom/docs/security/oracle-sql-firewall-faq.pdf

Comentários