Als PDF Downloaden!

Werbung
Tipps & Tricks: Dezember 2015
Bereich:
PL/SQL
Erstellung:
12/2015 HA
Versionsinfo:
11.2, 12.1
Letzte Überarbeitung:
12/2015 HA
SQL Injection erschweren durch DBMS_ASSERT
In PL/SQL empfiehlt es sich aus vielen Gründen, mit statischem SQL zu arbeiten, soweit das möglich ist; genannt
seien hier vor allem Wartbarkeit und Performance-Vorteile. Und so ganz nebenbei verwendet man automatisch
Bind-Variablen, ohne groß etwas dafür tun zu müssen: Übergabe-Parameter von Prozeduren und Funktionen
werden intern als Bind-Variablen umgesetzt, wenn sie in einem SQL-Befehl verwendet werden.
Nun gibt es aber Fälle, in denen man nicht um dynamisches SQL herumkommt. Solange dabei von außen
ausschließlich Werte übergeben werden, aber keine Spalten- oder Tabellennamen, kann man sich teilweise auch
hier mit einer Syntax behelfen, die Bind-Variablen benutzt: USING. Ein einfaches Beispiel dazu:
CREATE OR REPLACE PROCEDURE update_sal( p_ename IN emp.ename%TYPE, p_sal in
emp.sal%type)
IS
BEGIN
EXECUTE IMMEDIATE
'UPDATE EMP
SET sal = :a
WHERE ename = :b' using p_sal, p_ename;
END update_sal;
Oder allgemein:
EXECUTE IMMEDIATE '... WHERE spalte = :a' USING p_val;
Bei Ref Cursorn:
OPEN ref_cur FOR 'SELECT .... WHERE spalte = :a' USING p_val;
Das ist aber nicht immer praktikabel. Und spätestens dann, wenn Objektnamen oder Spaltennamen übergeben
werden, ist es vorbei mit der Verwendung von Bind-Variablen. Hier muss man mit Konkatenierung arbeiten.
Eine Möglichkeit, sich in solchen Fällen vor SQL Injection zu schützen, bietet das Package DBMS_ASSERT. Es
beinhaltet eine Reihe von Funktionen, die übergebene Werte nach unterschiedlichen Kriterien überprüfen oder
ändern.
Gehen wir einmal von folgender Funktion aus, die nur das Prinzip verdeutlichen soll (ausführlichere Beispiele zu
SQL Injection gibt es genügend im Netz):
CREATE TYPE vt AS TABLE OF VARCHAR2 (100);
/
CREATE OR REPLACE FUNCTION get_werte (p_such_spalte
IN VARCHAR2,
p_tabelle
IN VARCHAR2,
p_where_spalte IN VARCHAR2,
Muniqsoft GmbH
Schulungszentrum, Grünwalder Weg 13a, 82008 Unterhaching, Tel. 089 / 679090-40
IT-Consulting & Support, Witneystraße 1, 82008 Unterhaching, Tel. 089 / 6228 6789-0
Seite 1 von 6
p_wert
IN VARCHAR2)
RETURN vt
IS
v_cur SYS_REFCURSOR;
v_sql VARCHAR2 (2000);
v_arr vt;
BEGIN
v_sql := 'select ' || p_such_spalte || ' from ' || p_tabelle
|| ' where ' || p_where_spalte || ' = ''' || p_wert||'''';
OPEN v_cur FOR v_sql;
FETCH v_cur BULK COLLECT INTO v_arr;
CLOSE v_cur;
RETURN v_arr;
END get_werte;
/
SELECT COLUMN_VALUE
FROM TABLE (get_werte ('sal','scott.emp', 'ename', 'SMITH'))
/
COLUMN_VALUE
-------------------------------------------------------------------------------800
SELECT COLUMN_VALUE
FROM TABLE (get_werte ('sal','scott.emp', 'ename', 'SMITH'' OR 1=1 --'))
/
COLUMN_VALUE
-------------------------------------------------------------------------------800
...
1300
14 rows selected.
Es leuchtet ein, dass p_wert in anderer Form abgesichert werden muss als die übrigen drei Parameter. Mit
DBMS_ASSERT können beide Arten von Eingaben abgesichert werden:
Eingegebene Werte (wie p_wert) können mit ENQUOTE_LITERAL abgesichert werden
Eingegenebe Bezeichner (Spalten- und Tabellennamen wie die übrigen drei Parameter) können mit
ENQUOTE_NAME abgesichert werden
Ausserdem können eingegenebe Tabellennamen mit SIMPLE_SQL_NAME, QUALIFIED_SQL_NAME,
SQL_OBJECT_NAME und SCHEMA_NAME überprüft werden. Bei diesen Funktionen wird der übergebene Wert
nicht verändert, aber wenn die Überprüfung fehlschlägt, führt dies zu einem Fehler.
Diese Funktionen wollen wir uns nun näher anschauen.
ENQUOTE_LITERAL
Diese Funktion dient zur Absicherung eingegbener Literale, also String-Werte. Sie umgibt den übergebenen Wert
mit einfachen Hochkommata (die man dann natürlich im Quelltext nicht mehr mühsam ergänzen muss) und
Muniqsoft GmbH
Schulungszentrum, Grünwalder Weg 13a, 82008 Unterhaching, Tel. 089 / 679090-40
IT-Consulting & Support, Witneystraße 1, 82008 Unterhaching, Tel. 089 / 6228 6789-0
Seite 2 von 6
Diese Funktion dient zur Absicherung eingegbener Literale, also String-Werte. Sie umgibt den übergebenen Wert
mit einfachen Hochkommata (die man dann natürlich im Quelltext nicht mehr mühsam ergänzen muss) und
überprüft, ob er intern ein einzelnes einfaches Hochkomma enthält. Letzeres führt zu einem Fehler. Gepaarte
einfache Hochkommata sind erlaubt, weil ja auch Werte vorkommen können, die ein solches enthalten (Stichwort:
Escape).
Damit soll verhindert werden, dass durch die Hintertür eine Zusatzbedingung á la "SMITH' OR 1=1 --" mit
angegeben wird.
SELECT DBMS_ASSERT.ENQUOTE_LITERAL ( 'SMITH') FROM DUAL;
DBMS_ASSERT.ENQUOTE_LITERAL('SMITH')
-------------------------------------------------------------------------------'SMITH'
SELECT DBMS_ASSERT.ENQUOTE_LITERAL ( 'O''''Conner') FROM DUAL;
DBMS_ASSERT.ENQUOTE_LITERAL('O''''CONNER')
-------------------------------------------------------------------------------'O''Conner'
SELECT DBMS_ASSERT.ENQUOTE_LITERAL ( 'SMITH'' OR 1=1 --') FROM DUAL; -- Der klassische
Fall
Error at line 1
ORA-06502: PL/SQL: numerischer oder Wertefehler
ORA-06512: in "SYS.DBMS_ASSERT", Zeile 409
ORA-06512: in "SYS.DBMS_ASSERT", Zeile 493
ENQUOTE_NAME
In analoger Weise arbeitet ENQUOTE_NAME, nur dass hier keine einfachen Hochkommata ergänzt werden,
sondern doppelte. Standardmäßig wird der Bezeichner ausserdem in Großbuchstaben gewandelt, was aber
durch einen zweiten Parameter unterbunden werden kann.
Wird ein Wert mitgegeben, der bereits von doppelten Hochkommata eingeschlossen ist, wird kein zweites Paar
ergänzt, und die Schreibweise bleibt auch unverändert. Intern darf der Wert kein doppeltes Hochkomma
enthalten; in Version 11g konnte man noch Paare von doppelten Hochkommata mitgeben, in 12c wird auch das
unterbunden.
ENQUOTE_NAME dient rein der Absicherung der Eingabe: Alles, was sich innerhalb doppelter Hochkommata
befinden, wird von Oracle als ein Bezeichner betrachtet - womit auch gesagt ist, dass die Methode nicht tauglich
ist, wenn der Schemaname dem Objektnamen vorangestellt mitgegeben werden kann. Es findet keine inhaltliche
Überprüfung statt.
SELECT DBMS_ASSERT.ENQUOTE_NAME('emp') FROM DUAL;
DBMS_ASSERT.ENQUOTE_NAME('EMP')
-------------------------------------------------------------------------------"EMP"
SELECT DBMS_ASSERT.ENQUOTE_NAME('"emp"') FROM DUAL;
DBMS_ASSERT.ENQUOTE_NAME('"EMP"')
-------------------------------------------------------------------------------"emp"
SELECT DBMS_ASSERT.ENQUOTE_NAME('scott.emp') FROM DUAL;
DBMS_ASSERT.ENQUOTE_NAME('SCOTT.EMP')
Muniqsoft GmbH
Schulungszentrum, Grünwalder Weg 13a, 82008 Unterhaching, Tel. 089 / 679090-40
IT-Consulting & Support, Witneystraße 1, 82008 Unterhaching, Tel. 089 / 6228 6789-0
Seite 3 von 6
-------------------------------------------------------------------------------"SCOTT.EMP"
Mit diesen beiden Funktionen kann obiges Beispiel nun umgebaut werden:
CREATE OR REPLACE FUNCTION get_werte (p_such_spalte
IN VARCHAR2,
p_tabelle
IN VARCHAR2,
p_where_spalte IN VARCHAR2,
p_wert
IN VARCHAR2)
RETURN vt
IS
v_cur SYS_REFCURSOR;
v_sql VARCHAR2 (2000);
v_arr vt;
BEGIN
v_sql := 'select ' || DBMS_ASSERT.ENQUOTE_NAME(p_such_spalte)
|| ' from ' ||DBMS_ASSERT.ENQUOTE_NAME( p_tabelle)
|| ' where ' || DBMS_ASSERT.ENQUOTE_NAME(p_where_spalte)
|| ' = ' ||DBMS_ASSERT.ENQUOTE_LITERAL (p_wert);
OPEN v_cur FOR v_sql;
FETCH v_cur BULK COLLECT INTO v_arr;
CLOSE v_cur;
RETURN v_arr;
END get_werte;
/
SELECT COLUMN_VALUE FROM TABLE (get_werte ('sal','emp', 'ename', 'SMITH'))
/
COLUMN_VALUE
-------------------------------------------------------------------------------800
SELECT COLUMN_VALUE
FROM TABLE (get_werte ('sal','emp', 'ename', 'SMITH'' OR 1=1 --'))
/
Error at line 2
ORA-06502: PL/SQL: numerischer oder Wertefehler
ORA-06512: in "SYS.DBMS_ASSERT", Zeile 409
ORA-06512: in "SYS.DBMS_ASSERT", Zeile 493
ORA-06512: in "SCOTT.GET_WERTE", Zeile 11
SELECT COLUMN_VALUE FROM TABLE (get_werte ('sal','scott.emp', 'ename', 'SMITH'))
/
Error at line 1
ORA-00942: Tabelle oder View nicht vorhanden
SIMPLE_SQL_NAME und QUALIFIED_SQL_NAME
Diese Funktionen überprüfen ausschießlich, ob der eingegebene Wert den Oracle-Namenskonventionen
Muniqsoft GmbH
Schulungszentrum, Grünwalder Weg 13a, 82008 Unterhaching, Tel. 089 / 679090-40
IT-Consulting & Support, Witneystraße 1, 82008 Unterhaching, Tel. 089 / 6228 6789-0
Seite 4 von 6
Diese Funktionen überprüfen ausschießlich, ob der eingegebene Wert den Oracle-Namenskonventionen
entspricht. Nicht überprüft wird dabei allerdings die Länge der Eingabe.
Der Unterschied zwischen den beiden Funktionen besteht darin, dass SIMPLE_SQL_NAME einen einzelnen
Bezeichner erwartet, während QUALIFIED_SQL_NAME mit einem qualifizierten Oracle-Bezeichner umgehen
kann. Letzerer kann Punkte und "@" enthalten.
Da innerhab von doppelten Hochkommata alles erlaubt ist, kann man damit diese Art der Überprüfung damit
theoretisch aushebeln; allerdings taugt dann die Eingabe auch nicht mehr als Objektname.
SELECT DBMS_ASSERT.SIMPLE_SQL_NAME ( 'emp') FROM DUAL;
DBMS_ASSERT.SIMPLE_SQL_NAME('EMP')
-------------------------------------------------------------------------------emp
SELECT DBMS_ASSERT.SIMPLE_SQL_NAME('emp or 1=1') FROM DUAL;
Error at line 1
ORA-44003: Ungültiger SQL-Name
ORA-06512: in "SYS.DBMS_ASSERT", Zeile 206
SELECT DBMS_ASSERT.SIMPLE_SQL_NAME ( '"emp or 1=1"') FROM DUAL;
DBMS_ASSERT.SIMPLE_SQL_NAME('"EMPOR1=1"')
-------------------------------------------------------------------------------"emp or 1=1"
SELECT DBMS_ASSERT.QUALIFIED_SQL_NAME ( 'scott.emp@test') FROM DUAL;
DBMS_ASSERT.QUALIFIED_SQL_NAME('SCOTT.EMP@TEST')
-------------------------------------------------------------------------------scott.emp@test
SQL_OBJECT_NAME und SCHEMA_NAME
Hier findet auch eine inhaltliche Überprüfung statt. Das heisst, es müssen nicht nur die Namenskonventionen
eingehalten werden, sondern das Objekt bzw. das Schema muss auch vorhanden sein (Ausnahme: Angabe
eines Datenbank-Links).
Bei SQL_OBJECT_NAME kann ein Schemaname davor angegeben werden, muss aber nicht. Falls keiner
angegeben wird, muss sich das Objekt im aktuellen Schema befinden. Hat ein User keine Zugriffsrechte auf das
Objekt, schlägt die Überprüfung mit SQL_OBJECT_NAME ebenfalls fehl.
SCHEMA_NAME arbeitet case-sensitive, SQL_OBJECT_NAME dagegen nicht.
-- als User SCOTT:
SELECT DBMS_ASSERT.SQL_OBJECT_NAME ( 'emp') FROM DUAL;
DBMS_ASSERT.SQL_OBJECT_NAME('EMP')
-------------------------------------------------------------------------------emp
SELECT DBMS_ASSERT.SQL_OBJECT_NAME ( 'scott.emp') FROM DUAL:
DBMS_ASSERT.SQL_OBJECT_NAME('SCOTT.EMP')
-------------------------------------------------------------------------------scott.emp
SELECT DBMS_ASSERT.SQL_OBJECT_NAME ( 'DUMMY.TESTTAB') FROM DUAL;
Muniqsoft GmbH
Schulungszentrum, Grünwalder Weg 13a, 82008 Unterhaching, Tel. 089 / 679090-40
IT-Consulting & Support, Witneystraße 1, 82008 Unterhaching, Tel. 089 / 6228 6789-0
Seite 5 von 6
Error at line 1
ORA-44002: Ungültiger Objektname
ORA-06512: in "SYS.DBMS_ASSERT", Zeile 383
SELECT DBMS_ASSERT.SCHEMA_NAME ( 'scott') FROM DUAL;
Error at line 1
ORA-44001: Ungültiges Schema
ORA-06512: in "SYS.DBMS_ASSERT", Zeile 333
SELECT DBMS_ASSERT.SCHEMA_NAME ( 'SCOTT') FROM DUAL:
DBMS_ASSERT.SCHEMA_NAME('SCOTT')
-------------------------------------------------------------------------------SCOTT
Damit könenn wir unser obiges Beispiel nochmal umbauen, so dass es auch wieder mit vorangestellten
Schemanamen umgehen kann:
CREATE OR REPLACE FUNCTION get_werte (p_such_spalte
IN VARCHAR2,
p_tabelle
IN VARCHAR2,
p_where_spalte IN VARCHAR2,
p_wert
IN VARCHAR2)
RETURN vt
IS
v_cur SYS_REFCURSOR;
v_sql VARCHAR2 (2000);
v_arr vt;
BEGIN
v_sql := 'select ' || DBMS_ASSERT.ENQUOTE_NAME(p_such_spalte)
|| ' from ' ||DBMS_ASSERT.SQL_OBJECT_NAME( p_tabelle)
|| ' where ' || DBMS_ASSERT.ENQUOTE_NAME(p_where_spalte)
|| ' = ' ||DBMS_ASSERT.ENQUOTE_LITERAL (p_wert);
OPEN v_cur FOR v_sql;
FETCH v_cur BULK COLLECT INTO v_arr;
CLOSE v_cur;
RETURN v_arr;
END get_werte;
/
SELECT COLUMN_VALUE FROM TABLE (get_werte ('sal','scott.emp', 'ename', 'SMITH'))
/
COLUMN_VALUE
-------------------------------------------------------------------------------800
Fazit: Versierte Hacker werden wohl immer noch Mittel und Wege finden, aber schon allein der Einsatz
von DBMS_ASSERT.ENQUOTE_LITERAL macht ihnen das Leben deutlich schwerer.
Weitere Informationen zu SQL Injection erhalten Sie in unserem Security und APEX Security Kurs.
Muniqsoft GmbH
Schulungszentrum, Grünwalder Weg 13a, 82008 Unterhaching, Tel. 089 / 679090-40
IT-Consulting & Support, Witneystraße 1, 82008 Unterhaching, Tel. 089 / 6228 6789-0
Seite 6 von 6
Herunterladen