APEX-Nutzer "woanders" authentifizieren: Authentication Plugin am Beispiel "Cookie"
Erscheinungsmonat |
APEX-Version |
Datenbankversion |
Juli 2012 |
ab 4.1 |
ab 10.2 |
Wenn APEX-Anwendungen in eine IT-Landschaft eingebettet werden sollen, reicht die
APEX-Workspace-Authentifizierung vielfach nicht mehr aus. In vielen Fällen wird dann
die Authentifizierung mit einem LDAP-Server verwendet. Hierfür bringt APEX zwar ein
fertiges Authentifizierungsschema mit, allerdings erfordert dieses immer noch
die Eingabe des LDAP-Passworts auf der APEX-Anmeldeseite. Oft ist jedoch ein
Single-Sign-On (SSO) gewünscht - die Nutzer melden sich also einmal an einem Server an
und diese Anmeldung wird auch für die APEX-Anwendungen verwendet.
Das Besondere ist nun, dass das Darstellen der Anmeldeseite, die Entgegennahme von
Nutzername und Passwort und deren Überprüfung des Passworts außerhalb von APEX erfolgt -
die Anmeldeseite 101 hat keine Bedeutung mehr. Für bestimmte Umgebungen, wie bspw. den
Oracle Single-Sign-On-Server bringt APEX bereits fertige Authentifizierungsschemas mit.
Dieser Tipp stellt dagegen vor, wie man, mit einem Plugin, eine externe Authentifizierung in
seine APEX-Applikation einbinden kann. Der externe "Login-Server" ist in diesem Beispiel
selbst gebaut und sehr simpel. Die Einbindung in die APEX-Anwendung kann allerdings exemplarisch
für alle anderen Login-Server angesehen werden - oder mit anderen Worten: Auf diese Art und
Weise dürfte sich nahezu jeder Login-Server mit APEX nutzen lassen.
Bitte beachten Sie, dass der Code dieses Tipps als Beispielcode zu verstehen ist. Er soll dabei
helfen, die Vorgehensweise zur Erstellung von Authentifizierungsplugins zu erläutern. Auf keinen
Fall ist er ohne weiteres zum produktiven Einsatz geeignet - es gelten die
Terms Of Use des Oracle Technology Network für Beispielcode.
Als "externen Login-Server" nehmen wir zwei PL/SQL-Prozeduren, die mit dem Paket HTP, also wie
ein Java-Servlet, arbeiten. So
brauchen Sie zum Ausprobieren keinen zusätzlichen Server. Die Prozeduren werden
direkt, also an APEX vorbei, aufgerufen - das wirkt genauso, als ob es
ein externer Server, PHP, Java, .NET oder etwas völlig anderes wäre. Ein Login wird in diesem Beispiel nach
dem in Abbildung 1 skizzierten Schema ablaufen.
Abbildung 1: Ablaufdiagramm eines "selbstgebauten" Login-Verfahrens
Login-Server aufsetzen
Spielen Sie mit SQL*Plus oder dem SQL Workshop das folgende SQL-Skript ins Parsing Schema
Ihrer Anwendung ein. Es erzeugt die PL/SQL-Prozeduren LOGIN_PAGE und DO_LOGIN. Beide werden,
wie bereits erwähnt, direkt und an APEX vorbei aufgerufen. Wichtig ist daher die Vergabe
des EXECUTE-Privilegs an PUBLIC ganz unten im Skript.
Achten Sie darauf, den Parameter DOMAIN im Aufruf von OWA_COOKIE.SEND an Ihre Umgebung
anzupassen. Dieser Parameter legt fest, an welche Server der Browser das Cookie MY_REMOTE_USER senden
darf. Er muss so eingestellt werden, dass die APEX-Instanz das Cookie auch bekommt.
Die Prozedur LOGIN_PAGE stellt einfach nur eine einfache Login-Seite bereit. Beachten Sie die Parameter der
Prozedur - sie muss bereits mit Informationen über die APEX-Anwendung und die APEX-Session aufgerufen werden;
diese Informationen braucht sie zwar selbst nicht, sie werden aber an die nachfolgende Prozedur DO_LOGIN durchgereicht.
DO_LOGIN prüft zunächst, ob das Passwort gleich oracle ist - wenn ja, wird der Username in ein Cookie (MY_REMOTE_USER)
geschrieben und der Browser zur APEX-Anwendung zurückverwiesen. Stimmt das Passwort nicht, wird auf die Login-Seite
zurückverwiesen.
Natürlich ist es nicht besonders sicher, den Usernamen direkt ins Cookie zu schreiben - aber hier geht es
erstmal um die generelle Vorgehensweise. Im nächsten Schritt (weiter unten) werden wir das Verfahren dann
sicherer machen und nur noch ein "Session-Token" ins Cookie schreiben.
Den "externen Login-Server" in die APEX-Anwendung einbinden
Erstellen Sie zunächst eine einfache APEX-Anwendung mit nur einer Seite (die Instant Application reicht aus) - das Authentifizierungsverfahren
ist erstmal egal. Um den Erfolg der Integration prüfen zu können, fügen Sie der Anwendungsseite eine
HTML-Region mit folgender Regionsquelle hinzu:
Wenn Sie diese Anwendung zum ersten Mal starten, sollte das in etwa wie in Abbildung 2 aussehen (welcher
Username tatsächlich angezeigt wird, hängt natürlich vom verwendeten Login-Verfahren ab).
Abbildung 2: APEX-Anwendung zum Testen
Die Einbindung unseres "externen Login-Servers" implementieren wir direkt als Plugin, denn seit APEX 4.1
kann man auch Authentifizierungsschemas als Plugin erstellen. Für dieses Beispiel haben wir das Plugin bereits
vorbereitet und Sie können es
herunterladen und direkt als Plugin importieren. Navigieren Sie also zu den Gemeinsamen
Komponenten, dort zu den Plugins. Klicken Sie dann auf Importieren - darauf sollten Sie den in Abbildung 3
dargestellten Dialog sehen.
Abbildung 3: Authentifizierungs-Plugin importieren
Nach dem Import sehen Sie den folgenden Dialog mit den Details zum Plugin. Wie immer bei APEX Plugins, können Sie
auch dieses Plugin nach Belieben verändern oder erweitern (Abbildung 4).
Abbildung 4: Das Authentifizierungs-Plugin wurde importiert
Grundlage für unser Plugin ist eine
Forums-Diskussion, in der Patrick Wolf den Code des in APEX eingebauten
Authentifizierungsschemas HTTP Header veröffentlicht hat. Diesen Code könnte man nun so ändern,
dass anstelle einer HTTP-Header-Variablen ein Cookie ausgelesen wird. Des weiteren ist in dem Forums-Thread
auch die grundsätzliche Abfolge einer Authentifizierung erklärt ...
- Zuerst wird die Session-Sentry-Funktion aufgerufen. Deren Aufgabe ist es, festzustellen, ob die
aktuelle Session gültig ist oder nicht - dementsprechend gibt sie auch nur true oder false zurück.
In unserem Beispiel soll sie also prüfen, ob überhaupt schon eine APEX-Session und ein Username vorliegt und ob er
zu den Angaben im Cookie des "externen Login-Servers" passt. Wenn die Session gültig ist, liefert APEX die
gewünschte Seite aus, ansonsten wird zur Ungültige-Session-Funktion verzweigt.
- Die Ungültige-Session-Funktion prüft nun, ob ein Cookie vom "externen Login Server"
vorliegt und ob es in Ordnung ist. Wenn ja, wird eine APEX Session aufgebaut und mit APEX_AUTHENTICATION.LOGIN
"registriert". Damit liegt eine gültige Session vor und APEX kann die Anwendungsseite ausliefern.
- Die AJAX-Funktion ist für den externen Login-Server vorgesehen: Wenn dieser von sich aus (nach erfolgreichem Login)
die Funktion APEX_AUTHENTICATION.CALLBACK aufruft, wird der Code der AJAX-Funktion aufgerufen. In unserem Beispiel
verwenden wir sie nicht.
- Die Authentifizierungsfunktion wird genutzt, wenn das Login auf einer APEX-Seite (bspw. Seite 101)
und auch die Passwortprüfung in APEX erfolgt. Dann wird hier der Code zum Prüfen des Passworts hinterlegt.
- In der Post-Abmeldefunktion kann zusätzlicher Code zum Aufräumen von Session-Informationen eingetragen
werden. Auch diese verwenden wir in unserem Beispiel nicht.
Im Bereich PL/SQL-Code des Plugins ist demnach der folgende Code hinterlegt.
Die im Code enthaltenen Funktionen sind entsprechend bei Session-Sentry-Funktionsname und
Ungültiger Sessionfunktionsname eingetragen (Abbildung 5).
Abbildung 5: Im Plugin verwendete PL/SQL-Funktionen und PL/SQL Code
Im Plugin-Code sind keine hart kodierten Werte vorhanden; schließlich soll es
auch mit JSPs, PHP oder anderen "externen Login-Servern" zusammenarbeiten können.
Daher wird das Plugin parametrisiert - scrollen Sie herunter zum Abschnitt Attribute (Abbildung 6).
Abbildung 6: Attribute des Authentifizerungsplugins
Sie können das Plugin damit speichern und verlassen - hier ist nichts weiter zu tun. Nun
wollen wir das Plugin in der Anwendung nutzen. Dazu navigieren Sie nochmals zu den Gemeinsamen Komponenten
und dort zu den Authentifizierungsschemas (Abbildung 7).
Abbildung 7: In der Anwendung vorhandene Authentifizierungsschemas
Mit Klick auf Erstellen erzeugen Sie ein neues Schema. Im darauf folgenden Dialog
wählen Sie Basiert auf einem vorkonfigurierten Schema aus der Galerie aus und klicken
auf Weiter. Sie sehen daraufhin den in Abbildung 8 gezeigten Dialog.
Abbildung 8: Authentifizierungstyp auswählen
Neben den mit APEX standardmäßig ausgelieferten Authentifizierungstypen ist nun auch
unser Plugin Cookie-Based Authentication dabei. Wählen Sie es aus ...
Abbildung 9: Plugin Parameter einstellen
... und Sie sehen die Parameter des Plugins. Stellen Sie diese wie folgt ein.
- Der Cookie-Name ist MY_REMOTE_USER
- Bei fehlendem Cookie leiten Sie auf Eigene URL um.
- Die URL bei fehlerhaftem Cookie sollte nun auf die Login-Seite unseres zu Beginn dieses Tipps erstellten "externen Login-Servers" zeigen. In unserem Beispiel ist das also die PL/SQL-Prozedur LOGIN_PAGE. Geben Sie alle Parameter mit, die diese Seite erwartet - und die URL muss natürlich in einer Zeile sein.
Konkret könnte das so aussehen:
- Die PL/SQL-Funktion lassen Sie (noch) leer - denn (noch) steht der Username im Klartext im Cookie ...
- Schließlich soll das Cookie immer geprüft werden.
Nach dem Speichern ist das neue Authentifizierungsschema sofort aktiv (Abbildung 10).
Abbildung 10: Das neue Authentifizierungsschema ist nun aktiv
Nun können Sie testen - starten Sie Ihre Anwendung. Sie sollten auf die PL/SQL-Prozedur LOGIN_PAGE verzweigt werden (Abbildung 11).
Beachten Sie, dass Sie hier außerhalb des APEX-Kontexts sind - hier wäre, wie schon gesagt, auch eine PHP-Seite oder eine Java-Applikation
denkbar.
Abbildung 11: Bei Start der Anwendung sehen Sie die Login-Seite
Loggen Sie sich mit irgendeinem Namen und dem Passwort oracle ein. Daraufhin wird das Cookie gesetzt
und Sie werden an APEX zurückverzweigt. APEX liest den Usernamen aus dem Cookie aus und baut die Session
damit auf. Abbildung 12 zeigt die gleiche Anwendung wie Abbildung 2 zu Beginn, nun ist die Session allerdings
mit dem Usernamen aus dem Cookie aufgebaut.
Abbildung 12: Die APEX-Session wurde mit dem Usernamen aus dem Cookie aufgebaut
Sie können sich nun in der APEX-Anwendung bewegen, die APEX-Session bleibt erhalten. Selbst wenn Sie
die Session-ID aus der URL entfernen und die Seite neu laden (normalerweise würden Sie also ein neues
Login-Fenster sehen), bleibt die Session erhalten (denn das Cookie des "externen Login-Servers" ist ja
noch da. In den Browser-Einstellungen, im Bereich Cookies, können Sie das Cookie übrigens auch sehen - suchen
Sie nach einem Cookie namens MY_REMOTE_USER. Im in Abbildung 13 dargestellten Cookie hat sich jemand mit dem Benutzernamen test angemeldet.
Abbildung 13: Das Cookie MY_REMOTE_USER kann betrachtet werden
Das Cookie-Verfahren sicherer machen
Spätestens beim direkten Betrachten des Cookies wird die Schwachstelle dieses Ansatzes deutlich. Wenn man sich
das Cookie im Browser einfach so ansehen kann, dann kann man es auch manipulieren. Man könnte seinen Browser also
dazu bringen, dass er die APEX-Seite, mit einem Cookie namens MY_REMOTE_USER und gewünschten Inhalt, direkt
aufruft. APEX würde das Cookie erkennen und die Session aufbauen. Als erster Schritt war das also ganz gut - es reicht
aber nicht aus. Eine Variante wäre nun, den Usernamen im Cookie zu verschlüsseln - wir wollen hier aber noch
etwas weiter gehen. Dazu müssen wir zuerst einen etwas intelligenteren "externen Login-Server" schaffen.
Spielen Sie, wie zu Beginn dieses Tipps, mit SQL*Plus oder dem SQL Workshop das folgende Skript im Parsing
Schema Ihrer APEX-Anwendung ein. Die Prozedur DO_LOGIN wird nun ausgetauscht.
Wiederum ist das Passwort stets oracle. Allerdings wird der Username nun nicht einfach
ins Cookie, sondern mitsamt der APEX-Session-ID und der APEX-Anwendungs-ID in eine
Tabelle (MY_SESSIONS) geschrieben. Eine "Session-ID" wird als 20-Stellige Zufallszahl ermittelt (damit
man sie nicht erraten kann) und dann ins Cookie geschrieben. Eine Manipulation des
Cookies bringt nun nichts mehr, weil der Eintrag in die Tabelle MY_SESSIONS fehlt. Allerdings
muss APEX nun den Usernamen anhand des Session-ID ermitteln - wir brauchen also auch eine
"umgekehrte" Funktion.
Das ist hier natürlich nur deshalb so einfach, weil sich der "externe Login-Server" doch in
der gleichen Datenbank befindet wie die APEX-Anwendung. Wir müssen also nur in der Tabelle
MY_SESSIONS nachsehen. Komplizierter wird es, wenn der "Login Server" tatsächlich extern ist - dann
muss diese Funktion die Session-ID nochmals zum Login-Server senden, um den Usernamen herauszubekommen. Aber
das ist auf jeden Fall besser als das Übertragen desselben per Browser.
Der Rest ist nun einfach: Navigieren Sie in Ihrer Anwendung nochmals zu den Gemeinsamen Komponenten , dort
zu den Authentifizierungsschemas und dann zum neuen Authentifizierungsschema Cookie Auth. Tragen Sie
dann bei den Einstellungen die PL/SQL Funktion zum Ableiten des Usernamens ein: get_username_by_ext_sessionid (Abbildung 14).
Abbildung 14: PL/SQL-Funktion zum Ableiten des Usernamens in den Plugin-Einstellungen hinterlegen
Nach dem Abspeichern können Sie die Anwendung direkt neu starten. Wiederum werden Sie auf das
Login-Fenster verzweigt, wiederum müssen Sie sich mit einem beliebigen Usernamen und dem Passwort oracle anmelden, und
wiederum sehen Sie danach Ihre APEX Seite wie in Abbildung 12. Wenn Sie nun aber ins Cookie
schauen, sehen Sie den Unterschied (Abbildung 15).
Abbildung 15: Cookie Inhalt nach Absichern des Login-Verfahrens
Das Cookie enthält nun nur noch eine Zahl, die keinen Rückschluß auf den Usernamen zulässt. Man muss
Zugriff auf die Tabelle MY_SESSIONS haben, um ein Cookie manipulieren zu können. Außerdem haben wir
die Session-ID an die APEX-Applikation und an die APEX-Session gebunden. Wenn Sie nun die Session-ID
aus der APEX-URL entfernen, kann das Cookie nicht mehr validiert werden und Sie landen wieder auf
dem externen Login-Server. Das gleiche passiert, wenn Sie die Tabelle MY_SESSIONS leeren. Obwohl
die APEX-Session prinzipiell intakt ist, scheitert die Validierung des Cookies und der Anwender
muss sich neu einloggen. Ein Blick in die Tabelle MY_SESSIONS.
Weitere Anmerkungen
Nun ist unser "Cookie-Basiertes" Login-Verfahren schon ein gutes Stück sicherer geworden. Allerdings
gibt es noch ein paar Dinge zu beachten, die aber aus Platzgründen hier nicht
mehr ausführlich dargestellt werden können.
- Cookies werden zwischen Client und Server hin- und hergeschickt - auch wenn man aus
der Session-ID den Nutzer nicht mehr direkt ableiten kann: Die Verschlüsselung über HTTPS
ist sowohl für APEX als auch für den Login-Server ein Muss.
- Der Inhalt des Cookies könnte zusätzlich nochmals verschlüsselt werden. Den dazu Schlüssel
nötigen Schlüssel müssen nur die APEX-Anwendung und der Login-Server kennen. Gegebenenfalls
kann man den Schlüssel auch regelmäßig ändern.
- Die Tabelle MY_SESSIONS sollte mit Datenbankmitteln (wie der Virtual Private Database)
gegen unerwünschte Manipulationen abgesichert werden.
- Wie weiter oben bereits erwähnt, sehen Authentifizierungsplugins in APEX auch
eine Post-Abmeldefunktion vor - die in diesem Beispiel nicht ausprogrammiert wurde. Es
ist zu empfehlen, diese auszuprgrammieren und darin eine "Logout"-Funktion des Login-Server
aufzurufen (pseudocode).
Die PL/SQL-Prozedur LOGOUT_PAGE des "Login Servers" löscht dann das Cookie MY_REMOTE_USER
und invalidiert den Eintrag in der Tabelle MY_SESSIONS.
Fazit
APEX ist bereits seit dem ersten Tage in der Lage, sich in vorhandene Login-Verfahren zu integrieren, denn
seit der ersten Version HTML DB 1.5 gibt es Authentifizierungsschemas. Mit APEX 4.1 wurde diese Fähigkeit
lediglich neu organisiert und in die Plugin-Infrastruktur eingebunden, so dass sich Authentifizierungsschemas
elegant als Plugin kapseln lassen. Auf Basis des hier beschriebenen Cookie-Basierten Verfahrens dürfte sich
APEX mit jedem beliebigen Loginserver integrieren lassen - die hier angedachten Konzepte lassen sich durch
Ändern des Plugin Code in jede Richtung erweitern und verändern.
Zurück zur Community-Seite
|