This blog comes to complete the excellent blog written by my colleague Gregory Steulet on the same topic.
SQL FIREWALL is a new security layer integrated into Oracle Database 23c.
First, we create a admin user for which we grant it a new admin privilege “sql_firewall_admin”:
SQL> drop user if exists lfeadmin cascade;
User dropped.
SQL>
SQL> create user lfeadmin identified by lfeadmin;
User created.
SQL> grant create session to lfeadmin;
Grant succeeded.
SQL> grant sql_firewall_admin to lfeadmin;
Grant succeeded.
We enable the SQL FIREWALL:
SQL> prompt ENABLE SQL FIREWALL
ENABLE SQL FIREWALL
SQL> pause
SQL> conn lfeadmin/lfeadmin@//localhost:1521/freepdb1
Connected.
SQL> exec dbms_sql_firewall.enable;
PL/SQL procedure successfully completed.
SQL>
SQL> prompt CHECK THE STATUS OF SQL FIREWALL
CHECK THE STATUS OF SQL FIREWALL
SQL> pause
SQL> select status from dba_sql_firewall_status;
STATUS
--------
ENABLED
We create a capture process on the user LFE:
SQL> prompt CREATE CAPTURE FOR USER LFE
CREATE CAPTURE FOR USER LFE
SQL> pause
SQL> begin
  2    dbms_sql_firewall.create_capture (
  3  	 username	=> 'LFE',
  4  	 top_level_only => false,
  5  	 start_capture	=> true);
  6  end;
  7  /
PL/SQL procedure successfully completed.
We execute some sql from the user LFE:
SQL> conn LFE/LFE4DBIXCHANGE2023@//localhost:1521/freepdb1
Connected.
SQL> start demo_sql_firewall_load.sql
SQL> set echo on
SQL> set linesize 2000
SQL> prompt SESSION 1 - GET SID
SESSION 1 - GET SID
SQL> pause
SQL> SELECT SYS_CONTEXT ('USERENV', 'SID') FROM DUAL;
SYS_CONTEXT('USERENV','SID')
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
44
SQL> prompt SESSION 1 - UPDATE PRICE FOR PRODUCT A1 --> PRICE = PRICE+10
SESSION 1 - UPDATE PRICE FOR PRODUCT A1 --> PRICE = PRICE+10
SQL> pause
SQL> select price from products where product='A1';
     PRICE
----------
      1629
SQL> update products set price = price+10 where product= 'A1';
1 row updated.
SQL> select price from products where product='A1';
     PRICE
----------
      1629
SQL>
We stop the capture and we check the capture logs:
SQL> prompt STOP THE CAPTURE
STOP THE CAPTURE
SQL> pause
SQL> exec dbms_sql_firewall.stop_capture('LFE');
PL/SQL procedure successfully completed.
SQL>
SQL> prompt CHECK CAPTURE LOGS
CHECK CAPTURE LOGS
SQL> pause
SQL> set linesize 150 pagesize 40
SQL> column command_type format a12
SQL> column current_user format a15
SQL> column client_program format a45
SQL> column os_user format a10
SQL> column ip_address format a10
SQL> column sql_text format a30
SQL>
SQL> select command_type,
  2  	    current_user,
  3  	    client_program,
  4  	    os_user,
  5  	    ip_address,
  6  	    sql_text
  7  from   dba_sql_firewall_capture_logs
  8  where  username = 'LFE';
COMMAND_TYPE CURRENT_USER CLIENT_PROGRAM OS_USER    IP_ADDRESS SQL_TEXT
------------ --------------- --------------------------------------------- --
SELECT	     LFE	     sqlplus@db23c (TNS V1-V3) oracle     127.0.0.1  SELECT SYS_CONTEXT (:"SYS_B_0",:"SYS_B_1") FROM DUAL
SELECT	     LFE	     sqlplus@db23c (TNS V1-V3) oracle     127.0.0.1  SELECT PRICE FROM PRODUCTS WHERE PRODUCT=:"SYS_B_0"
UPDATE	     LFE	     sqlplus@db23c (TNS V1-V3) oracle     127.0.0.1  UPDATE PRODUCTS SET PRICE=PRICE +:"SYS_B_0" WHERE PRODUCT=:"SYS_B_1"
Now we generate an allow list based on the capture logs:
SQL> exec dbms_sql_firewall.generate_allow_list ('LFE');
PL/SQL procedure successfully completed.
SQL>
SQL> prompt CHECK THE ALLOW LIST AND NEW DATA DICTIONARY VIEWS
CHECK THE ALLOW LIST AND NEW DATA DICTIONARY VIEWS
SQL> pause
SQL> column username format a20
SQL> select * from  dba_sql_firewall_allowed_ip_addr where username = 'LFE';
USERNAME	     IP_ADDRESS
-------------------- ----------
LFE		     127.0.0.1
SQL>
SQL> column os_program format a50
SQL> select * from dba_sql_firewall_allowed_os_prog where username = 'LFE';
USERNAME	     OS_PROGRAM
-------------------- --------------------------------------------------
LFE		     sqlplus@db23c (TNS V1-V3)
SQL>
SQL> column os_user format a10
SQL> select * from dba_sql_firewall_allowed_os_user where username = 'LFE';
USERNAME	     OS_USER
-------------------- ----------
LFE		     oracle
SQL>
SQL> column sql_text format A50
SQL> select current_user,sql_text from dba_sql_firewall_allowed_sql where username = 'LFE';
CURRENT_USER	SQL_TEXT
--------------- --------------------------------------------------
LFE		UPDATE PRODUCTS SET PRICE=PRICE +:"SYS_B_0" WHERE
		PRODUCT=:"SYS_B_1"
LFE		SELECT PRICE FROM PRODUCTS WHERE PRODUCT=:"SYS_B_0
		"
LFE		SELECT SYS_CONTEXT (:"SYS_B_0",:"SYS_B_1") FROM DU
		AL
The allow list stores the sql statement captures plus his context (IP ADDRESS, OS_PROGRAM, OS_USER).
We check this allow list and decides which sql statement / context authorized or un-authorized to be executed and we enable the allow list, we use the option “dbms_sql_firewall.enforfe_all” to authorize all : the sql statement plus the context. There are different list of options.
SQL> begin
  2    dbms_sql_firewall.enable_allow_list (
  3  	 username => 'LFE',
  4  	 enforce  => dbms_sql_firewall.enforce_all,
  5  	 block	  => true);
  6  end;
  7  /
PL/SQL procedure successfully completed.
SQL> select username,
    	    status,
    	    top_level_only,
    	    enforce,
    	    block
    from   dba_sql_firewall_allow_lists
    where username='LFE';
USERNAME	     STATUS   TOP_LEVEL_ONLY ENFORCE	     BLOCK
-------------------- -------- -------------- --------------- --------------
LFE		     ENABLED  N 	     ENFORCE_ALL     Y
Now, SQL which are in the allow list are authorized:
SQL> select price from products where product='A1';
     PRICE
----------
      1629
But SQL which are not in the allow list are un-authorized:
SQL> select price,product from products where product='A1';
select price,product from products where product='A1'
                          *
ERROR at line 1:
ORA-47605: SQL Firewall violation
We check the sql firewall violation log table:
select sql_text,
           firewall_action,
           ip_address,
           cause,
           occurred_at
    from   dba_sql_firewall_violations
    where  username = 'LFE';
SQL_TEXT
--------------------------------------------------------------------------------
FIREWAL IP_ADDRESS					 CAUSE
------- ------------------------------------------------ --------------------
OCCURRED_AT
---------------------------------------------------------------------------
SELECT PRICE,PRODUCT FROM PRODUCTS WHERE PRODUCT=:"SYS_B_0"
Blocked 127.0.0.1					 SQL violation
27-SEP-23 06.30.31.875532 PM +00:00
SQL>