Es ist in der Tat möglich: Ich kann mich an der Oracle-Datenbank als User "A" mit
dem Passwort des Benutzers "B" anmelden - und das ist keine Sicherheitslücke. Ich
möchte dieses Blog-Posting dem Thema Proxy Authentication widmen. Das kann man
ganz besonders in einer dreischichtigen Webarchitektur gebrauchen. Denn dort werden Datenbankverbindungen
nahezu immer statisch im Application Server konfiguriert ( data-sources.xml). Wenn
man in einer solchem Umgebung mit den Nutzerkonten der Datenbank arbeiten möchte, hat
man ein Problem: Man kann sie nicht alle in der data-sources.xml einrichten; erstmal
sind es oft zu viele und zweitens kann man die data-sources.xml nicht so einfach im
laufenden Betrieb ändern. Die Lösung ist Proxy Authentication: Dabei wird die
Datenbankverbidnung
im Application Server fest mit einem technischen User und einem Passwort eingerichtet;
der tatsächliche Datenbankuser, nach dem sich auch die Privilegien richten, wird
zur Laufzeit festgelegt. Und das ganze funktioniert wie folgt: Zuerst brauchen wir
einen technischen User TECHUSER (mit Passwort TECHUSER) und zwei "echte" User (REALUSER1 und
REALUSER2).
create user techuser identified by techuser / grant connect to techuser / reate user realuser1 identified by realuser1 / grant connect, resource to realuser1 / reate user realuser2 identified by realuser2 / grant connect, resource to realuser2 /
Und damit die User in einer Anwendung unterscheidbar werden, bekommen Sie nun eine Kopie
der EMP-Tabelle, aber mit unterschiedlichen Inhalten ...
craete table realuser1.emp as select * from scott.emp where deptno = 10 / craete table realuser2.emp as select * from scott.emp where deptno = 20 /
Und jetzt geht es los: Zuerst muss man der Datenbank sagen, dass TECHUSER die Erlaubnis
bekommt, sich mit seinem eigenen Passwort als REALUSER1 oder REALUSER2 "auszugeben". Man kann
auch sagen: TECHUSER wird der Proxy User für die Clients REALUSER1 und REALUSER2.
alter user realuser1 grant connect through techuser / alter user realuser2 grant connect through techuser /
Der Setup lässt sich in der Dictionary View PROXY_USERS auch überprüfen:
SQL> select * from proxy_users; PROXY CLIENT AUT FLAGS --------------- --------------- --- ----------------------------------- TECHUSER REALUSER1 NO PROXY MAY ACTIVATE ALL CLIENT ROLES TECHUSER REALUSER2 NO PROXY MAY ACTIVATE ALL CLIENT ROLES
Mit SQL*Plus kann man das jetzt schon ausprobieren:
D:\>sqlplus.exe techuser[realuser1]/techuser SQL*Plus: Release 11.1.0.6.0 - Production on Do Dez 22 16:17:30 2011 Copyright (c) 1982, 2007, Oracle. All rights reserved. Verbunden mit: Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - Production With the Partitioning, Oracle Label Security, OLAP, Data Mining and Real Application Testing options SQL> select user from dual; USER ------------------------------ REALUSER1
Man sieht sehr schön, dass Username und Passwort des TECHUSER zum Login
verwendet werden; in eckigen Klammern wird aber festgelegt, für wen die
Datenbankverbindung eigentlich aufgemacht werden soll.
So weit, so gut, so einfach. Aber für eine dreischichtige Applikation nutzt das
bis jetzt noch gar nichts. Schließlich kann man nicht einfach den "echten" User
in eckige Klammern in die Definition der Datenbankverbindung eintragen, denn diese
ist ja statisch. Der User, für den der Connect gelten soll, soll sich aber beliebig
ändern können - also dynamisch sein. Also müssen wir in der Lage sein, den Usernamen,
den man bei SQL*Plus in eckige Klammern setzt, per Java-Code zu setzen ... Schauen wir
uns zunächst ein kleines Java-Testprogramm an: Es gibt zuerst den Namen des angemeldeten
Users aus ( select user from dual) und dann die Inhalte der EMP Tabelle.
Achtet darauf, dass Username und Passwort des TECHUSER hart kodiert sind - und das wird
so bleiben!
import java.sql.*; import oracle.jdbc.*; import java.io.*; import java.util.*; public class proxyConnect { public static void main(String args[]) throws Exception { DriverManager.registerDriver(new oracle.jdbc.OracleDriver()); Connection con = DriverManager.getConnection("jdbc:oracle:thin:@sccloud030:1521/orcl","techuser","techuser"); /* * Code zum Setzen des Client-Usernamens HIER !!! */ Statement stmt = null; ResultSet rs1 = null; stmt = con.createStatement(); rs1 = stmt.executeQuery("select user from dual"); while (rs1.next()) { System.out.println("Database userid: " + rs1.getString(1)); } rs1.close(); stmt.close(); stmt = con.createStatement(); rs1 = stmt.executeQuery("select * from emp"); while (rs1.next()) { System.out.println("EMPNO: " + rs1.getString(1) + " ["+rs1.getString(2)+"] - DEPTNO: "+rs1.getString("DEPTNO")); } rs1.close(); stmt.close(); con.close(); } }
Beim ersten Test sagt uns das Programm nur, dass wir als TECHUSER verbunden sind und
dass es keine EMP-Tabelle gibt ...
D:\> java.exe proxyConnect realuser1 Database userid: TECHUSER Exception in thread "main" java.sql.SQLSyntaxErrorException: ORA-00942: Tabelle oder View nicht vorhanden at oracle.jdbc.driver.SQLStateMapping.newSQLException(SQLStateMapping.java:91) at oracle.jdbc.driver.DatabaseError.newSQLException(DatabaseError.java:112) at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:173) :
Dann bauen wir jetzt den Code ein, der den "richtigen" User setzt. Tauscht die Zeilen ...
/* * Code zum Setzen des Client-Usernamens HIER !!! */
... durch diese hier aus: Als Usernamen nehmen wir den ersten Parameter der Kommandozeile.
Properties props = new Properties(); props.put("PROXY_USER_NAME", args[0]); ((OracleConnection)con).openProxySession(OracleConnection.PROXYTYPE_USER_NAME, props);
Neu kompilieren und wieder ausführen. Und .. voilá:
D:\> java proxyConnect realuser1 Database userid: REALUSER1 EMPNO: 7782 [CLARK] - DEPTNO: 10 EMPNO: 7839 [KING] - DEPTNO: 10 EMPNO: 7934 [MILLER] - DEPTNO: 10 D:\> java proxyConnect realuser2 Database userid: REALUSER2 EMPNO: 7369 [SMITH] - DEPTNO: 20 EMPNO: 7566 [JONES] - DEPTNO: 20 EMPNO: 7788 [SCOTT] - DEPTNO: 20 EMPNO: 7876 [ADAMS] - DEPTNO: 20 EMPNO: 7902 [FORD] - DEPTNO: 20
Nur durch die Angabe des Namens wird nun festgelegt, als welcher User die Datenbanksession
laufen soll. Damit kann ein Java-Programm (bspw. in einem Application Server) mit einer statischen
Datasource-Definition dennoch dynamisch den Datenbankuser wechseln. Denn eine bestehende
Proxy-Verbindung kann geschlossen werden, um danach auf der gleichen "physikalischen" Datenbankverbindung
eine neue Proxy-Verbindung für einen anderen User zu öffnen. Im folgenden Code
habe ich das mal illustriert - zur besseren Übersicht habe ich die Datenbankaktionen
entfernt und gegen den Pseudocall auf erledigeDatenbankaktionen() ausgetauscht.
public static void main(String args[]) throws Exception { // Physikalische Verbindung öffnen als TECHUSER DriverManager.registerDriver(new oracle.jdbc.OracleDriver()); Connection con = DriverManager.getConnection("jdbc:oracle:thin:@sccloud030:1521/orcl","techuser","techuser"); // Proxy-Verbindung für User REALUSER1 öffnen, etwas tun und schließen Properties props = new Properties(); props.put("PROXY_USER_NAME", "realuser1"); ((OracleConnection)con).openProxySession(OracleConnection.PROXYTYPE_USER_NAME, props); erledigeDatenbankaktionen(con); ((OracleConnection)con).close(OracleConnection.PROXY_SESSION); // Die physikalische Verbindung ist immer noch offen ... // Proxy-Verbindung für User REALUSER2 öffnen, etwas tun und schließen props.put("PROXY_USER_NAME", "realuser2"); ((OracleConnection)con).openProxySession(OracleConnection.PROXYTYPE_USER_NAME, props); erledigeDatenbankaktionen(con); ((OracleConnection)con).close(OracleConnection.PROXY_SESSION); // Physikalische Verbindung schließen con.close(); }
Beim Umstieg von Client/Server-
zu Webanwendungen ist ein solches Vorgehen hochinteressant.
Wichtig ist, dass REALUSER1 bzw. REALUSER2 immer noch das CREATE SESSION-Privileg brauchen; auch
darf der Account nicht gelockt sein. Es ist aber durchaus möglich, direkte Connects zu unterbinden -
dazu muss man "nur" das Passwort auf einen nicht ermittelbaren Wert setzen ...
SQL> alter user realuser1 identified by values 'Hallo'
Nun wird der Text "Hallo" als Hashwert in die Oracle-Passworttabelle geschrieben - ein Connect als
REALUSER1 ist nun theoretisch möglich: wenn man das Passwort herausbekommt, dessen Hashwert
"Hallo" ergibt. Defacto jedoch kann man sich nur noch über den TECHUSER als REALUSER1 anmelden.
Aber auch ein Parallelbetrieb mit direkten und Proxy-Verbindungen ist natürlich problemlos
machbar.
Mehr zur Proxy Authentication in der Dokumentation: Dazu sind zwei Links wichtig:
- Security Guide: Using a Middle Tier Server for Proxy Authentication
http://docs.oracle.com/cd/E11882_01/network.112/e16543/authentication.htm#i1010326 - JDBC Developer's Guide: Proxy Authentication
http://docs.oracle.com/cd/E11882_01/java.112/e16548/proxya.htm#CHDHHAAD