SQL-Firewall (23c)

02.
Januar
2024
Veröffentlicht von: Richard Meinhardt

Auch mit der Datenbank Version 23c wird die Palette der Security-Features erweitert. Unter anderem kommt das Feature der SQL-Firewall dazu. Damit können Sie, wie der Name schon vermutet lässt, (fast alle) SQL-Statements filtern und somit seine Datenbank sichern und schützen.

Grundlagen

SQL-Firewall ist ein Echtzeitschutz, der dafür designt wurde SQL-Statements zu überprüfen. Dies geschieht vor der Ausführung des SQL, und es kann zu den für eine Firewall typischen Ergebnissen kommen:

  • Allow: Die Ausführung des Statements wird ohne weitere Aktionen erlaubt.
  • Allow und Log: Die Ausführung des Statements wird erlaubt aber diese Aktion aufgezeichnet.
  • Block und Log: Die Ausführung des Statements wird verboten und diese Aktion aufgezeichnet.

Grundsätzlich läuft das Erstellen der Regeln aber ein wenig anders als bei einer klassischen Firewall. Bei der SQL-Firewall müssen Sie die Regeln nicht von Hand verfassen. Der Ablauf, um SQL-Firewall Regeln zu erstellen gliedert sich in 3 Schritte.

  1. Schritt 1: SQL-Statements aufzeichnen
  2. Schritt 2: Allow-List erzeugen
  3. Schritt 3: Allow-List aktivieren

Neben der Möglichkeit, SQL-Statements zu regulieren, gibt es auch Möglichkeiten, die Verbindungen selbst einzuschränken. Dafür gibt es drei mögliche Attribute: IP-Adresse, Betriebssystem Benutzer und Betriebssystem Programm.

Zur Konfiguration der Firewall gibt es zwei Möglichkeiten

  • Oracle Data Safe
  • PL/SQL Package DBMS_SQL_FIREWALL

Der größte Vorteil von Oracle Data Safe ist, dass Sie mehrere Firewalls zentral administrieren können. Im folgenden Beispiel werden wir uns aber auf das PL/SQL Package beschränken.

Grundlegendes Beispiel

In diesem Beispiel wird anhand von zwei Benutzern die grundlegende Funktion der SQL-Firewall demonstriert, einen Benutzer für die Administration und einem normalen Benutzer.

Der Administrator braucht neben der CONNECT Rolle auch die Rolle SQL_FIREWALL_ADMIN. Diese erlaubt ihm, das entsprechende Package zu verwenden und enthält ein Leserecht auf die DBA_* SQL-Firewall Views.

SQL> CREATE USER FWADMIN IDENTIFIED BY fwadmin DEFAULT TABLESPACE USERS TEMPORARY TABLESPACE TEMP;

User created.

SQL> GRANT SQL_FIREWALL_ADMIN TO FWADMIN;

Grant succeeded.

SQL> GRANT CONNECT TO FWADMIN;

Grant succeeded.

SQL> conn FWADMIN/fwadmin@freepdb
Connected.
SQL> show user
USER is "FWADMIN"
SQL>

Der normale Benutzer braucht keine besonderen Berechtigungen und erhält somit neben der CONNECT Rolle ein CREATE TABLE Recht für eine Test-Tabelle und einen spezielles SELECT Recht, damit er diese Tabelle, einfacher befüllen kann.

SQL> CREATE USER FWENDUSER IDENTIFIED BY fwenduser DEFAULT TABLESPACE USERS TEMPORARY TABLESPACE TEMP;

User created.

SQL> GRANT CONNECT TO FWENDUSER;

Grant succeeded.

SQL> GRANT CREATE TABLE TO FWENDUSER;

Grant succeeded.

SQL> GRANT SELECT ON DBA_OBJECTS TO FWENDUSER;

Grant succeeded.

SQL> ALTER USER FWENDUSER QUOTA UNLIMITED ON USERS;

User altered.

SQL> conn FWENDUSER/fwenduser@freepdb
Connected.
SQL> DROP TABLE IF EXISTS test_table;

Table dropped.

SQL> CREATE TABLE test_table AS SELECT * FROM DBA_OBJECTS;

Table created.

SQL> select count(*) from test_table

  COUNT(*)
----------
    108320

SQL> show user
USER is "FWENDUSER"
SQL>

Im Folgenden werden die meisten Befehle mit dem FWADMIN Benutzer ausgeführt. Lediglich die SQL-Befehle, die aufgezeichnet werden müssen, führt der FWENDUSER aus.

Als erstes muss die SQL-Firewall, als FWADMIN, aktiviert werden:

SQL> EXEC DBMS_SQL_FIREWALL.ENABLE;

PL/SQL procedure successfully completed.

SQL>

Danach müssen Sie ein CAPTURE starten:

SQL> BEGIN
DBMS_SQL_FIREWALL.CREATE_CAPTURE (
        username         => 'FWENDUSER',
        top_level_only   => TRUE,
        start_capture    => TRUE
);
END;
/

PL/SQL procedure successfully completed.

SQL>

Falls Sie den CAPTURE nicht gleich starten wollen, können Sie "start_capture" auf FALSE setzten und ihn später mit „EXEC DBMS_SQL_FIREWALL.START_CAPTURE ('FWENDUSER');“ starten.

Nun müssten Sie im Normalfall abwarten. In unserem Beispiel werden wir folgende SQL-Befehle absetzten (als FWENDUSER):

SQL> select object_name from test_table where OWNER='SYS';

OBJECT_NAME
--------------------------------------------------------------------------------
I_FILE#_BLOCK#
...
WRH$_ACTIVE_SESSION_HISTORY_PK

87777 rows selected.

SQL>
SQL> select count(*) from test_table;

  COUNT(*)
----------
    108320

SQL> select count(owner) from test_table;

COUNT(OWNER)
------------
      108320

SQL> INSERT INTO test_table (OWNER) VALUES ('ASDF123');

1 row created.

SQL> UPDATE test_table SET OBJECT_NAME = 'HALLO' WHERE OWNER='ASDF123';

1 row updated.

SQL> commit;

Commit complete.

SQL> variable varo varchar2(50) = 'ASDF123'
SQL> select object_name from test_table where OWNER=:varo;

OBJECT_NAME
--------------------------------------------------------------------------------
HALLO

SQL>

Darauffolgend beenden Sie den CAPTURE mit:

QL> EXEC DBMS_SQL_FIREWALL.STOP_CAPTURE ('FWENDUSER');

PL/SQL procedure successfully completed.

SQL>

Danach können Sie mit folgendem SELECT die aufgezeichnete SQL-Statements anzeigen.

SQL> set linesize 1000
SQL> set pagesize 1000
SQL> col sql_text for a70
SQL> col CLIENT_PROGRAM for a40
SQL> col OS_USER for a10
SQL> col COMMAND_TYPE for a15
SQL> SELECT COMMAND_TYPE, SQL_TEXT, CLIENT_PROGRAM, OS_USER, IP_ADDRESS from DBA_SQL_FIREWALL_CAPTURE_LOGS order by COMMAND_TYPE;

COMMAND_TYPE    SQL_TEXT                                                               CLIENT_PROGRAM                           OS_USER    IP_ADDRESS
--------------- ---------------------------------------------------------------------- ---------------------------------------- ---------- ------------------------------------------------
INSERT          INSERT INTO TEST_TABLE (OWNER) VALUES (:"SYS_B_0")                     sqlplus@tl007.muniqsoft.de (TNS V1-V3)   oracle     10.10.4.9
SELECT          SELECT OBJECT_NAME FROM TEST_TABLE WHERE OWNER=:VARO                   sqlplus@tl007.muniqsoft.de (TNS V1-V3)   oracle     10.10.4.9
SELECT          SELECT COUNT (*) FROM TEST_TABLE                                       sqlplus@tl007.muniqsoft.de (TNS V1-V3)   oracle     10.10.4.9
SELECT          SELECT OBJECT_NAME FROM TEST_TABLE WHERE OWNER=:"SYS_B_0"              sqlplus@tl007.muniqsoft.de (TNS V1-V3)   oracle     10.10.4.9
SELECT          SELECT COUNT (OWNER) FROM TEST_TABLE                                   sqlplus@tl007.muniqsoft.de (TNS V1-V3)   oracle     10.10.4.9
UPDATE          UPDATE TEST_TABLE SET OBJECT_NAME=:"SYS_B_0" WHERE OWNER=:"SYS_B_1"    sqlplus@tl007.muniqsoft.de (TNS V1-V3)   oracle     10.10.4.9

6 rows selected.

SQL>

Dabei kann man schon sehr schnell erkennen, dass beim Aufzeichnen der SQL-Statements alle Literale durch Bind Variablen ersetzt werden. Alles andere wäre auch, aus offensichtlichen Gründen ziemlich ungünstig.

In einem "echten" Szenario, wäre das der Moment, an dem Sie entscheiden müssen, wie lange Sie aufzeichnen. Dafür gibt es leider keinen allgemein gültigen Wert, da sich das je nach Applikation und Benutzung der Datenbank stark unterscheidet. Wenn Sie zum Beispiel Jährliche/Monatliche Prozesse in Ihrer Datenbank haben, müssen Sie schauen, dass diese auch alle mit aufgezeichnet werden, sonst kann es sein, dass Sie Statements blocken, welche für den normalen Betrieb nötig sind.

Nachdem Sie die Aufzeichnungen überprüft haben, müssen Sie das CAPTURE zu einer ALLOW_LIST umwandeln:

SQL> SELECT count(*) FROM DBA_SQL_FIREWALL_ALLOWED_SQL WHERE USERNAME = 'FWENDUSER';

  COUNT(*)
----------
         0

SQL> EXEC DBMS_SQL_FIREWALL.GENERATE_ALLOW_LIST ('FWENDUSER');

PL/SQL procedure successfully completed.

SQL> SELECT count(*) FROM DBA_SQL_FIREWALL_ALLOWED_SQL WHERE USERNAME = 'FWENDUSER';

  COUNT(*)
----------
         6

SQL>

Danach finden Sie die gleichen SQL-Statements die wir schon in DBA_SQL_FIREWALL_CAPTURE_LOGS gesehen haben, in DBA_SQL_FIREWALL_ALLOWED_SQL:

SQL> set linesize 1000
SQL> set pagesize 1000
SQL> col sql_text for a70
SQL> SELECT SQL_TEXT from DBA_SQL_FIREWALL_ALLOWED_SQL;

SQL_TEXT
----------------------------------------------------------------------
UPDATE TEST_TABLE SET OBJECT_NAME=:"SYS_B_0" WHERE OWNER=:"SYS_B_1"
SELECT OBJECT_NAME FROM TEST_TABLE WHERE OWNER=:VARO
INSERT INTO TEST_TABLE (OWNER) VALUES (:"SYS_B_0")
SELECT OBJECT_NAME FROM TEST_TABLE WHERE OWNER=:"SYS_B_0"
SELECT COUNT (OWNER) FROM TEST_TABLE
SELECT COUNT (*) FROM TEST_TABLE

6 rows selected.

SQL>

Sobald eine ALLOW_LIST vorhanden ist, können Sie die Firewall "scharf" schalten:

SQL> BEGIN
  DBMS_SQL_FIREWALL.ENABLE_ALLOW_LIST (
    username       => 'FWENDUSER',
    enforce        => DBMS_SQL_FIREWALL.ENFORCE_ALL,
    block          => TRUE
   );
END;
/

PL/SQL procedure successfully completed.

SQL>

Nachdem die Firewall somit vollkommen aktiv ist, können wir unsere SQL-Statements nochmal ausführen.

Die im Verlauf des Tipps verwendeten Statements haben kein Problem damit, aber was ist wenn wir diese Statements ein wenig anpassen?

SQL> select count(*), owner from test_table group by owner;
select count(*), owner from test_table group by owner
                            *
ERROR at line 1:
ORA-47605: SQL Firewall violation

SQL> select count(OBJECT_NAME) from test_table;
select count(OBJECT_NAME) from test_table
                               *
ERROR at line 1:
ORA-47605: SQL Firewall violation

SQL> select owner from test_table where OWNER='SYS';
select owner from test_table where OWNER='SYS'
                  *
ERROR at line 1:
ORA-47605: SQL Firewall violation

SQL> INSERT INTO test_table (OBJECT_NAME) VALUES ('ASDFOBJECT');
INSERT INTO test_table (OBJECT_NAME) VALUES ('ASDFOBJECT')
            *
ERROR at line 1:
ORA-47605: SQL Firewall violation

SQL> INSERT INTO test_table (OWNER) VALUES ('ASDF123456');

1 row created.

SQL> UPDATE test_table SET OWNER = 'HALLO' WHERE OBJECT_NAME='ASDF123';
UPDATE test_table SET OWNER = 'HALLO' WHERE OBJECT_NAME='ASDF123'
       *
ERROR at line 1:
ORA-47605: SQL Firewall violation

SQL> commit;

Commit complete.

SQL> variable varo varchar2(50) = 'ASDF123123'
SQL> select object_name from test_table where OWNER=:varo;

no rows selected

SQL>

Wie zu erwarten, erhalten wir bei allen -  bis auf bei zwei Statements - eine aussagekräftige Fehlermeldung: ORA-47605: SQL Firewall violation.

Neben der Möglichkeit SQL-Statements einzuschränken können Sie auch die Verbindung zur Datenbank selbst einschränken. Im einfachsten Fall können Sie zum Beispiel die IP-Adresse für diesen User auf die IP des Applikationsserver einschränken.

SQL> BEGIN
  DBMS_SQL_FIREWALL.ADD_ALLOWED_CONTEXT (
    username       => 'FWENDUSER',
    context_type   => DBMS_SQL_FIREWALL.IP_ADDRESS,
    value          => '192.0.2.1'
   );
END;
/

PL/SQL procedure successfully completed.

SQL> BEGIN
  DBMS_SQL_FIREWALL.DELETE_ALLOWED_CONTEXT (
    username       => 'FWENDUSER',
    context_type   => DBMS_SQL_FIREWALL.IP_ADDRESS,
    value          => '10.11.9.1'
   );
END;
/

PL/SQL procedure successfully completed.

SQL>
SQL> col USERNAME for a20
SQL> col IP_ADDRESS for a20
SQL> select USERNAME, IP_ADDRESS from DBA_SQL_FIREWALL_ALLOWED_IP_ADDR;

USERNAME             IP_ADDRESS
-------------------- --------------------
FWENDUSER            192.0.2.1

SQL>

Anschließend wird ein Login Versuch von einer anderen IP-Adresse wieder mit der folgenden Fehlermeldung quittiert: ORA-47605: SQL Firewall violation.

[oracle@tl007 ~]$ sqlplus FWENDUSER/fwenduser@freepdb

SQL*Plus: Release 23.0.0.0.0 - Developer-Release on Wed Dec 6 15:38:08 2023
Version 23.2.0.0.0

Copyright (c) 1982, 2023, Oracle.  All rights reserved.

ERROR:
ORA-47605: SQL Firewall violation

Alle Firewall Violations sind in der View DBA_SQL_FIREWALL_VIOLATIONS zu sehen:

SQL> set linesize 1000
SQL> set pagesize 1000
SQL> col username for a10
SQL> col command_type for a10
SQL> col sql_text for a80
SQL> SELECT USERNAME, COMMAND_TYPE, SQL_TEXT, FIREWALL_ACTION, CAUSE, OCCURRED_AT from DBA_SQL_FIREWALL_VIOLATIONS order by occurred_at;

USERNAME   COMMAND_TY SQL_TEXT                                                                         FIREWAL CAUSE                OCCURRED_AT
---------- ---------- -------------------------------------------------------------------------------- ------- -------------------- ---------------------------------------------------------------------------
FWENDUSER  SELECT     SELECT COUNT (*),OWNER FROM TEST_TABLE GROUP BY OWNER                            Blocked SQL violation        06-DEC-23 04.35.40.730032 PM +02:00
FWENDUSER  SELECT     SELECT COUNT (OBJECT_NAME) FROM TEST_TABLE                                       Blocked SQL violation        06-DEC-23 04.35.45.730213 PM +02:00
FWENDUSER  SELECT     SELECT OWNER FROM TEST_TABLE WHERE OWNER=:"SYS_B_0"                              Blocked SQL violation        06-DEC-23 04.35.48.993945 PM +02:00
FWENDUSER  INSERT     INSERT INTO TEST_TABLE (OBJECT_NAME) VALUES (:"SYS_B_0")                         Blocked SQL violation        06-DEC-23 04.35.53.729654 PM +02:00
FWENDUSER  UPDATE     UPDATE TEST_TABLE SET OWNER=:"SYS_B_0" WHERE OBJECT_NAME=:"SYS_B_1"              Blocked SQL violation        06-DEC-23 04.36.01.474351 PM +02:00
FWENDUSER  CONNECT                                                                                     Blocked Context violation    06-DEC-23 04.38.08.366624 PM +02:00

6 rows selected.

SQL>

CAPTURE zweckentfremden

Selbst wenn Sie nicht vorhaben, die SQL-Firewall einzusetzen, können Sie den CAPTURE auch einfach nur zum Reevaluieren von Zugriffrechten nutzen. Gerade in Datenbanklandschaften mit sehr vielen User, die untereinander stark verknüpft sind, ist es oft nicht klar, welche Rechte genau gebraucht werden, so dass es schnell zu einem „SELECT/INSERT/UPDATE/DELETE ANY TABLE“ kommt (oder noch schlimmer: DBA-Rechten). Aus Sicht der Security ist dies natürlich zu vermeiden, und dabei kann Sie dieser Teil der SQL-Firewall unterstützen, indem Sie einfach die Inhalte des CAPTURE als Grundlage für ein Rechte-Review nutzen.
 

Fazit

Die in 23c neu hinzugekommen SQL-Firewall ist ein weiterer Bestandteil im Werkzeugkasten, um die Datenbank sicherer zu machen. Gerade Lücken in Richtung SQL-Injektion können mit der SQL-Firewall vorgebeugt werden. Es ist aber wie bei jedem Security Feature, wenn Sie nicht aufpassen, schaden Sie mehr als Sie helfen. Sollten Sie Probleme bei Ihrer SQL-Firewall oder anderen Oracle Themen haben stehen wie Ihnen gern zu Seite.

Jede Menge Know-how für Sie!

In unserer Know-How Datenbank finden Sie mehr als 300 ausführliche Beiträge zu den Oracle-Themen wie DBA, SQL, PL/SQL, APEX und vielem mehr.
Hier erhalten Sie Antworten auf Ihre Fragen.