[ORACLE] Rowlevel Security mit SYS_CONTEXT

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.

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
 

Neue Beiträge

Zurück