Exceptionfault
Erfahrenes Mitglied
Ich habe mir mal wieder ein kleines aber hoffentlich spannendes Thema rausgesucht und ein kleines Tutorial geschrieben. Grob geht es um Sicherheit von Daten in unserer Datenbank, und zwar auf Zeilenbene. Häufig haben wir es mit Werten in Tabellen zu tun, von denen wir nicht unbedingt jedem unbeschränkten Zugriff erlauben möchten. Ich habe mir das Beispiel der Gehaltstabelle eines Unternehmens ausgesucht.
Die folgende Tabelle hat als Felder die Mitarbeiternummer, den Namen des Mitarbeiters und dessen Gehalt.
Nun ist es nicht schlimm jedem Mitarbeiter Zugriff auf seinen Datensatz zu gewähren, schliesslich kann so jeder kontrollieren ob z.B. die Adresse noch stimmt oder was auch immer... Auf keinen Fall sollte der Mitarbeiter aber das Gehalt oder andere Daten seiner Kollegen sehen. Wie macht man nun sowas ? Klar, der einfachste Weg wäre über eine Anwendung, die einfach nur den entsprechenden Datensatz abholt. Dann bleibt mir aber immer nocht der Weg mich direkt an die Datenbank anzumelden und einfach die Tabelle komplett abzufragen.
Ein anderes Problem ist z.B. wie handle ich die Sicherheit bei Webanwendungen wo sich alle Benutzer normalerweise den selben Datenbankbenutzer teilen ? Abhilfe schaffen hier Views, d.h. ich gewähre dem Benutzer selbst kein leserecht auf die Tabellen, sondern nur auf Views. Und diese Views muss ich nun nur noch einschränken auf das was der aktuelle User sehen darf.
Ein recht komfortabler Ansatz ist hier über den sog. SYS_CONTEXT zu arbeiten, das lässt sich ungefähr mit Umgebungsvariablen vergleichen, in denen ich beispielsweise die Rechte des aktuellen Benutzers ablege. Oracle bietet nebenbei auch Verfahren wie VPD (Virtual Private Database) aber das ist leider ein Lizenzpflichtiges Feature, dass Oracle sich gut bezahlen lässt.
Wir machen es einfacher und legen erstmal eine Berechtigungstabelle an, in der wir über Benutzername und Passwort eine Mitarbeiternummer feststellen können. Somit sind wir vollkommen unabhängig vom eigentlichen Datenbankbenutzernamen.
Was wir nun noch brauchen ist eine Prozedur mit der wir den Namen und das Passwort kontrollieren und die entspr. Umgebungsvariable (SYS_CONTEXT) setzen.
Man sieht, unser Package hat genau eine Procedure. Dieser muss man Name und Passwort übergeben und je nach Erfolg wird der Sys_context 'MYCONTEXT'.'userid' auf 0 oder die Mitarbeiternummer gesetzt. Als nächstes müssen wir überhaupt den Context erstellen, auf den wir später zugreifen wollen. Der Context erfordert als Angabe ein Package, eben unser Package MYPACK. Das muss deshalb sein, weil später nur Funktionen aus dem Package MYPACK den erstellen Context ändern dürfen. D.h. der Benutzer selbst kann den Context nur über die von uns bereitgestellten Prozeduren in MYPACK ändern. in diesem Fall klappt das auch nur mit richtigem Benutzernamen und Passwort.
Und zum guten Schluss die View, mit der wir unsere Gehaltstabelle filtern.
Legen wir nun einen Anwendungs-Benutzer an, braucht dieser lediglich das EXECUTE Recht auf unser Package sowie Leserechte auf diese View. Auf keinen Fall darf er natürlich Rechte auf der zu Grunde liegenden Tabelle haben.
Und nun testen wir das ganze mal praktisch:
Die folgende Tabelle hat als Felder die Mitarbeiternummer, den Namen des Mitarbeiters und dessen Gehalt.
Code:
CREATE TABLE SALARY (
USERID NUMBER(10,0) NOT NULL,
USERN VARCHAR2(30) NOT NULL,
SALARY NUMBER(20,2) NOT NULL
);
INSERT INTO SALARY VALUES ( 23, 'H. Tester', 3500);
INSERT INTO SALARY VALUES ( 24, 'H. Tester 2', 1500);
Nun ist es nicht schlimm jedem Mitarbeiter Zugriff auf seinen Datensatz zu gewähren, schliesslich kann so jeder kontrollieren ob z.B. die Adresse noch stimmt oder was auch immer... Auf keinen Fall sollte der Mitarbeiter aber das Gehalt oder andere Daten seiner Kollegen sehen. Wie macht man nun sowas ? Klar, der einfachste Weg wäre über eine Anwendung, die einfach nur den entsprechenden Datensatz abholt. Dann bleibt mir aber immer nocht der Weg mich direkt an die Datenbank anzumelden und einfach die Tabelle komplett abzufragen.
Ein anderes Problem ist z.B. wie handle ich die Sicherheit bei Webanwendungen wo sich alle Benutzer normalerweise den selben Datenbankbenutzer teilen ? Abhilfe schaffen hier Views, d.h. ich gewähre dem Benutzer selbst kein leserecht auf die Tabellen, sondern nur auf Views. Und diese Views muss ich nun nur noch einschränken auf das was der aktuelle User sehen darf.
Ein recht komfortabler Ansatz ist hier über den sog. SYS_CONTEXT zu arbeiten, das lässt sich ungefähr mit Umgebungsvariablen vergleichen, in denen ich beispielsweise die Rechte des aktuellen Benutzers ablege. Oracle bietet nebenbei auch Verfahren wie VPD (Virtual Private Database) aber das ist leider ein Lizenzpflichtiges Feature, dass Oracle sich gut bezahlen lässt.
Wir machen es einfacher und legen erstmal eine Berechtigungstabelle an, in der wir über Benutzername und Passwort eine Mitarbeiternummer feststellen können. Somit sind wir vollkommen unabhängig vom eigentlichen Datenbankbenutzernamen.
Code:
CREATE TABLE AUTHENTICATE (
USERID NUMBER(10,0) NOT NULL,
USERNAME VARCHAR2(25) NOT NULL,
PASSWORD VARCHAR2(25) NOT NULL
);
INSERT INTO AUTHENTICATE VALUES ( 23, 'tester', 'test' );
INSERT INTO AUTHENTICATE VALUES ( 24, 'tester2', 'test2' );
Was wir nun noch brauchen ist eine Prozedur mit der wir den Namen und das Passwort kontrollieren und die entspr. Umgebungsvariable (SYS_CONTEXT) setzen.
Code:
CREATE OR REPLACE PACKAGE MYPACK
AUTHID DEFINER
AS
PROCEDURE authorize( v_username IN VARCHAR2,
v_password IN VARCHAR2 );
END;
/
CREATE OR REPLACE PACKAGE BODY MYPACK
AS
PROCEDURE authorize( v_username IN VARCHAR2,
v_password IN VARCHAR2 ) AS
v_userid NUMBER;
BEGIN
SELECT USERID
INTO v_userid
FROM AUTHENTICATE
WHERE USERNAME = v_username
AND PASSWORD = v_password;
DBMS_SESSION.SET_CONTEXT (
namespace => 'MYCONTEXT',
attribute => 'userid',
value => TO_CHAR( v_userid )
);
EXCEPTION
WHEN OTHERS THEN
DBMS_SESSION.SET_CONTEXT (
namespace => 'MYCONTEXT',
attribute => 'userid',
value => '0'
);
END;
END;
/
Man sieht, unser Package hat genau eine Procedure. Dieser muss man Name und Passwort übergeben und je nach Erfolg wird der Sys_context 'MYCONTEXT'.'userid' auf 0 oder die Mitarbeiternummer gesetzt. Als nächstes müssen wir überhaupt den Context erstellen, auf den wir später zugreifen wollen. Der Context erfordert als Angabe ein Package, eben unser Package MYPACK. Das muss deshalb sein, weil später nur Funktionen aus dem Package MYPACK den erstellen Context ändern dürfen. D.h. der Benutzer selbst kann den Context nur über die von uns bereitgestellten Prozeduren in MYPACK ändern. in diesem Fall klappt das auch nur mit richtigem Benutzernamen und Passwort.
Code:
CREATE OR REPLACE CONTEXT mycontext USING MYPACK;
Und zum guten Schluss die View, mit der wir unsere Gehaltstabelle filtern.
Code:
CREATE OR REPLACE VIEW V_SALARY AS
SELECT *
FROM SALARY
WHERE USERID = SYS_CONTEXT('MYCONTEXT', 'userid');
Legen wir nun einen Anwendungs-Benutzer an, braucht dieser lediglich das EXECUTE Recht auf unser Package sowie Leserechte auf diese View. Auf keinen Fall darf er natürlich Rechte auf der zu Grunde liegenden Tabelle haben.
Und nun testen wir das ganze mal praktisch:
Code:
SQL> EXEC MYPACK.AUTHORIZE('tester', 'test');
PL/SQL-Prozedur wurde erfolgreich abgeschlossen.
SQL> SELECT * FROM V_SALARY;
USERID USERN SALARY
---------- ------------------------------ ----------
23 H. Tester 3500
SQL> EXEC MYPACK.AUTHORIZE('tester2', 'test2');
PL/SQL-Prozedur wurde erfolgreich abgeschlossen.
SQL> SELECT * FROM V_SALARY;
USERID USERN SALARY
---------- ------------------------------ ----------
24 H. Tester 2 1500
SQL> EXEC MYPACK.AUTHORIZE('tester2', 'blurb');
PL/SQL-Prozedur wurde erfolgreich abgeschlossen.
SQL> select * from V_SALARY;
Es wurden keine Zeilen ausgewõhlt