In Zeiten des Datendiebstahls und der Bespitzelung sollten Anwendungsentwickler besonderen Wert auf die Sicherheit der gespeicherten Daten legen. Gerade personenbezogene Daten und Passwörter bedürfen einem besonderen Schutz auf aller tiefster Ebene: der Datenbank. Oracle stellt einen Mechanismus genannt Virtual Private Database (VPD) zur Verfügung den ich hier etwas näher erläutern möchte.
Als Beispiel nutzen wir eine einfach Benutzertabelle mit ID, Username und Passwort:
CREATE TABLE users ( id NUMBER PRIMARY KEY NOT NULL, username VARCHAR2(30) NOT NULL, password VARCHAR2(64) NOT NULL ); INSERT INTO users VALUES ( 1, 'USERA', 'strenggeheim' ); INSERT INTO users VALUES ( 2, 'USERB', 'meinPasswort' ); INSERT INTO users VALUES ( 3, 'USERC', 'usercspwd' );
Selbstverständlich sollten Passwörter niemals unverschlüsselt in der Datenbank stehen, für dieses Beispiel ist dies aber eher Nebensache.
VPD nutzt zur Sicherung der Daten sogenannte Policies die in Form von PL/SQL Prozeduren definiert werden. Die Prozeduren zeichnen sich dadurch aus, dass sie immer einen String im Format einer gültigen SQL WHERE Klausel zurückliefern. Die folgende Prozedur unterscheidet zwischen den angemeldeten Benutzern. Heißt der aktuelle Benutzer “ADMIN“, so liefert sie die WHERE Klausel “1=1″ zurück, ansonsten “USER = USERNAME”.
CREATE OR REPLACE FUNCTION pwdpolicy(
object_schema IN VARCHAR2,
object_name IN VARCHAR2)
RETURN VARCHAR2
IS
BEGIN
IF USER = 'ADMIN' THEN
RETURN '1 = 1';
ELSE
RETURN 'USER = USERNAME';
END IF;
END;
Welche Auswirkungen die WHERE Klauseln haben sehen wir später. Zunächst muss die Policy noch an die Tabelle gebunden werden:
begin
DBMS_RLS.ADD_POLICY (
object_name => 'USERS',
policy_name => 'PASSWORD_POLICY',
policy_function => 'PWDPOLICY');
end;
Die Auswirkung der Policy sehen nun am folgenden Beispiel, wenn wir uns mit 2 unterschiedlichen Benutzern an der Datenbank anmelden. Einmal dem Benutzer ADMIN und dem Benutzer USERB.
ADMIN> select * from admin.users; ID USERNAME PASSWORD --- ---------- --------------- 1 USERA strenggeheim 2 USERB meinPasswort 3 USERC usercspwd USER> select * from admin.users; ID USERNAME PASSWORD --- ---------- --------------- 2 USERB meinPasswort
Bei beiden Benutzern ist die Policy wirksam, jedoch durch die stets wahre Bedingung “1=1” werden im ersten Fall alle Zeilen zurückgeliefert. Im zweiten Fall mit Benutzer USERB nur die Zeilen, die auch zum Benutzer USERB gehören. Die zurückgelieferte Bedingung in der Policy kann dabei beliebig komplex werden und darf z.B. auch Subselects usw. enthalten. Ungeschickt ist, dass USERB nun nicht mehr in der Lage ist die Datensätze der anderen Benutzer zu sehen, obwohl wir ja eigentlich nur die Passwörter der anderen Benutzer geheim halten wollen.
Glücklicherweise lässt sich VPD jedoch auch auf Spaltenebene eingrenzen. Im folgenden habe ich die Policy leicht geändert und als relevante Spalte die PASSWORD Spalte angegeben.
begin
DBMS_RLS.DROP_POLICY( NULL, 'USERS', 'PASSWORD_POLICY');
DBMS_RLS.ADD_POLICY (
object_name => 'USERS',
policy_name => 'PASSWORD_POLICY',
policy_function => 'PWDPOLICY',
sec_relevant_cols => 'PASSWORD');
end;
/
USER> select id, username, password from admin.users;
ID USERNAME PASSWORD
--- ---------- --------------
2 USERB meinPasswort
USER> select id, username from admin.users;
ID USERNAME
--- ---------
1 USERA
2 USERB
3 USERC
Das Ergebnis ist erfreulich. Will USERB ein Passwort wissen und nimmt die Spalte in das SELECT Statement auf, so greift die Policy und liefert ihm nur seinen Datensatz zurück. Lässt er jedoch die Spalte mit den sensiblen Daten weg, so darf er alle Datensätze in der Tabelle betrachten. Eine weiter Option zur Spaltensicherheit zeigt das folgende Beispiel:
begin
DBMS_RLS.DROP_POLICY( NULL, 'USERS', 'PASSWORD_POLICY');
DBMS_RLS.ADD_POLICY (
object_name => 'USERS',
policy_name => 'PASSWORD_POLICY',
policy_function => 'PWDPOLICY',
sec_relevant_cols => 'PASSWORD',
sec_relevant_cols_opt => DBMS_RLS.ALL_ROWS);
end;
/
USERB>select * from admin.users
ID USERNAME PASSWORD
--- ---------- --------------
1 USERA
2 USERB meinPasswort
3 USERC
Durch die Option “sec_relevant_cols_opt => DBMS_RLS.ALL_ROWS” werden dem Benutzer stets alle Datensätze zurückgeliefert. Lediglich für die Spalte PASSWORD greift dann die Policy und liefert je nach Auswertung entweder ein NULL oder den Wert zurück.
Schlagworte: Oracle, PL/SQL, Policies, RLS, Virtual Private Database, VPD