Benutzeranmeldungen und die Vergabe von Zugriffsrechten an Benutzer finden sich in nahezu allen größeren Business-Anwendungen. Dabei wird zunächst die Identität des Benutzers ermittelt, dieser Vorgang wird auch als Authentisierung des Benutzers bezeichnet. Das Ergebnis der Authentisierung entscheidet, ob dem Benutzer Zugriff auf das System gewährt wird oder ob der Zugang abgelehnt wird. Über diesen grundlegenden Zugangsschutz hinaus sind häufig weitergehende Berechtigungen mit der Identität eines Benutzers verbunden. So kann für jeden Benutzer detailliert festgelegt werden, welche Funktionalität des Systems genutzt werden darf. Diese Zuteilung von Berechtigungen an Benutzer wird als Autorisierung bezeichnet.
Komponenten zur Authentisierung von Benutzern werden in der Praxis häufig mit jeder neuen Anwendung neu entwickelt. Die Schnittstellen sind meist speziell auf die zugehörige Anwendung zugeschnitten und die entstehenden Komponenten somit nicht in anderen Anwendungen wieder verwendbar. Sollen in einer Anwendung weitere Techniken zur Benutzeridentifikation unterstützt werden, bedeutet dies immer die Neuentwicklung eines entsprechenden Moduls und dessen Integration in die bestehende Anwendung - Anwendungen sind damit nicht unabhängig von der verwendeten Login-Technologie. Ähnlich verhält es sich mit der Vergabe von Berechtigungen. Diese werden ebenfalls meist anwendungsspezifisch implementiert oder erfolgen vollständig außerhalb der Anwendung in den beteiligten Backend-Systemen.
Standardisierung
Die geschilderten Tatsachen zeigen die Notwendigkeit zur Definition einheitlicher Schnittstellen für Authentisierung und Autorisierung. Mit dem Java Authentication and Authorization Service (JAAS) hat Sun diesem Gedanken Rechnung getragen. Wie der Name erahnen lässt, besteht JAAS aus zwei wesentlichen Teilen.Der für die Authentisierung zuständige Teil von JAAS stellt eine standardisierte Schnittstelle zur Verfügung, die durch Anwendungen zum Zugriff auf Authentisierungs-Dienste genutzt werden kann. Diese Schnittstelle ist völlig Technologie unabhängig. Die verschiedenen Verfahren zur Identifikation eines Benutzers werden dabei als Login-Module realisiert. Zur Verwendung einer neuen Identifikationsmethode (z.B. Smartcard anstelle von Benutzername/Passwort) können diese Module durch einen Administrator ausgetauscht werden. Anpassung oder Rekompilieren der Anwendung sind hierzu nicht notwendig. Vielmehr beschränkt sich der Austausch auf die Bereitstellung der benötigten .class-Dateien und die Konfiguration des Moduls in einer JAAS- Konfigurationsdatei. JAAS stellt hiermit eine Java-Implementierung des aus dem UNIX-Umfeld bekannten Pluggable Authentication Moduls (PAM) [7] dar.
Die Autorisierungskomponente ergänzt die in Java2 implementierten Code-zentrierten Mechanismen der Zugriffskontrolle um Benutzer-zentrierte Mechanismen. Ohne JAAS war die Vergabe von Zugriffsrechten in Java2 an die Herkunft des auszuführenden Codes gekoppelt (Codebase) oder/und an dessen Herausgeber (Signer). JAAS weitet dieses Konzept auf den Benutzer einer Anwendung aus - die Berechtigungen für sensible Operationen können damit in Abhängigkeit von der Identität des Ausführenden vergeben werden. Seit der Version 1.4.0 der Java-Plattform ist der Java Authentication and Authorization Service fester Bestandteil der Laufzeitumgebung. Für die ältere Version 1.3.* kann JAAS als Standard-Extension installiert werden; auch die Enterprise Edition der Java-Plattform verwendet JAAS als integralen Bestandteil für Authentisierung und Autorisierung. Für Client-, Web- und EJB-Container J2EE-konformer Produkte ist die Unterstützung von JAAS gemäß der Spezifikation verpflichtend.
Benutzerinformationen
Für Anwendungen, die eine auf JAAS basierende Benutzeranmeldung implementieren, stellt das API Klassen zur Verfügung, die dem Zugriff auf Informationen über den angemeldeten Benutzer einerseits und der Durchführung des Login-Vorgangs andererseits dienen.Als zentrale Klasse zum Zugriff auf die im Rahmen des Logins ermittelten Benutzerinformationen stellt JAAS die Klasse Subject zur Verfügung. Ein Subject repräsentiert eine Instanz, welche im Rahmen des Login-Prozesses identifiziert wurde. Dabei kann es sich sowohl um Personen, als auch um Systeme handeln. Eine identifizierte Instanz verfügt über einen oder mehrere Namen. Diese werden in Klassen abgelegt, die das Interface Principal aus dem Paket java.security implementieren. Darüber hinaus sind mit einem Subject häufig auch sicherheitsrelevante Informationen verknüpft. Dabei kann es sich um öffentlich zugängliche Informationen wie Public Keys und Zertifikate oder auch um geheime Informationen wie Private Keys und Passworte handeln. Diese Informationen werden als Credentials bezeichnet. Credentials können beliebige Java-Objekte sein, für die es kein gemeinsam zu implementierendes Interface gibt.
Angemeldet
Zur Durchführung des Login-Vorgangs stellt JAAS die Klasse LoginContext zur Verfügung. Das Codefragment in Listing 1 zeigt deren Verwendung. JAAS-basierte Anwendungen starten den Anmeldevorgang durch die Instanziierung eines LoginContextes. Der Parameter, der hierbei übergeben wird, verweist auf eine JAAS-Konfiguration. Wie später beschrieben wird, legt diese für die verschiedenen Anwendungen fest, welche Login-Module zu verwenden sind. Durch Aufruf der Methode login wird die Authentisierung unter Verwendung der konfigurierten Login-Module gestartet. Nach einer erfolgreichen Anmeldung kann über den LoginContext ein Subject ermittelt werden, welches die ermittelten Benutzerinformationen (Principals und Credentials) enthält. Misslungene Anmeldeversuche werden durch eine LoginException quittiert. Principals und Credentials, die im Rahmen der Authentisierung mit einem Subject verknüpft wurden, werden nach Aufruf der logout-Methode wieder gelöscht.Listing 1: JAAS-basiertes Login
// Get a new login contextLoginContext ctx = new LoginContext( "DemoApp" );try {// perform the loginctx.login();System.out.println( "Login suceeded" );// get the authenticated subjectSubject subject = ctx.getSubject();...// all done -> logoutctx.logout();}catch( LoginException exc ) {System.out.println( "Login failed" );}
Baukasten
Die Login-Module pictureen die Bausteine, aus denen die Benutzeranmeldung einer Anwendung zusammengesetzt wird, dabei sind Anwendung und Login-Module dennoch voneinander unabhängig. Sowohl das Austauschen der verwendeten Module einer Anwendung als auch die Verwendung der Module in unterschiedlichen Anwendungen ist möglich. Voraussetzung hierfür ist eine einheitliche Schnittstelle aller Login-Module. Diese wird durch das Interface LoginModule definiert. Zur Entwicklung eines neuen Login-Moduls muss lediglich eine Klasse erstellt werden, welche die fünf Methoden dieses Interfaces implementiert.
Zweiphasig
Abhängig von der verwendeten Login-Konfiguration müssen alle oder eine Teilmenge der konfigurierten Login-Module den Benutzer erfolgreich identifizieren. Aus diesem Grund erfolgt die Anmeldung eines Benutzers in zwei Phasen. In der ersten Phase wird durch den LoginContext die login-Methode eines jeden Moduls aufgerufen. Dabei führen die Login-Module eine Identifikation des Benutzers durch. Zu diesem Zeitpunkt erfolgt jedoch noch keine Zuweisung von Principals oder Credentials an das Subject. Erst wenn alle login-Aufrufe erfolgreich waren, wird in der zweiten Phase für jedes Login-Module die commit-Methode ausgeführt. Dies sorgt für eine Verknüpfung der Benutzerinformationen mit dem Subject. Führte die erste Phase zu einem Fehler wird in der zweiten Phase die abort-Methode der Login-Module aufgerufen. Dies gibt den Login-Modulen die Gelegenheit die ermittelten Principals und Credentials wieder zu löschen. Der Abmeldung eines Benutzers dient die Methode logout. Ein zweiphasiger Ablauf wie bei der Anmeldung ist hierbei jedoch nicht notwendig.Eingabe gefordert
Üblicherweise ist im Rahmen der Identifikation eines Benutzers eine Interaktion mit diesem notwendig. Dabei kann es sich um die Eingabe von Benutzernamen und Passwort handeln oder auch nur um die Aufforderung, einen Finger auf den Fingerabdruck-Scanner zu legen. Die Implementierung von Ein- und Ausgabe muss zur Realisierung einer einheitlichen Benutzerschnittstelle sinnvoller Weise durch die Anwendung erfolgen. Um der Anforderung nach Anwendungsunabhängigkeit gerecht zu werden, muss JAAS Mechanismen bereitstellen, die es erlauben, Benutzerinteraktionen außerhalb eines Login-Moduls zu implementieren, diese aber innerhalb der Module zu steuern. Der in JAAS beschrittene Weg hierfür heißt Callbacks. JAAS definiert für die Verwendung von Callbacks zwei Interfaces. Callback ist ein Tag-Interface ohne eigene Methoden. Klassen, die dieses Interface implementieren, stellen Objekte dar, die der Anwendung Informationen liefern, was für eine Interaktion benötigt wird. Darüber hinaus fungieren sie als Datenaustauschobjekt zwischen Anwendung und Login-Module. JAAS beinhaltet bereits Callbacks für verschiedene Zwecke. In Listing 2 werden NameCallback und PasswordCallback zur Ermittlung von Benutzernamen und Passwort verwendet. CallbackHandler werden von Anwendungen implementiert und durch Login-Module aufgerufen. Der zu verwendende CallbackHandler wird einem Login-Modul beim Aufruf der initialize-Methode bekannt gemacht.Listing 2: Verwendung von Callbacks im Login-Modul
public class SampleLogin implements LoginModule{CallbackHandler handler;...public boolean login() throws LoginException{// Get username and passwordCallback[] cb = new Callback[ 2 ];cb[ 0 ] = new NameCallback( "Username:" );cb[ 1 ] = new PasswordCallback( "Password:", false );handle.handle( cb );// Verify username and passwordverify( ( (NameCallback) cb[ 0 ] ).getName(),( (PasswordCallback) cb[ 1 ].getPassword() );return( true );}private void verify( String user, String password )throws LoginException{boolean loginOK;...if( !loginOk ) {throw new LoginException( "Login error" );}}}
public class SampleHandlerimplements CallbackHandler{public void handle( Callback[] cb ){String user;String password;//Username/password requiredif( cb.length == 2 &&cb[ 0 ] instanceof NameCallback &&cb[ 1 ] instanceof PasswordCallback ) {....cb[ 0 ].setName( username );cb[ 1 ].setPassword( password.getBytes() );}// Password only (i.e. Smartcard PIN)else if( cb.length == 1 &&cb[ 0 ] instanceof PasswordCallback ) {...cb[ 0 ].setPassword( pin );}// Callback unsupportedelse {throw new UnsupportedCallbackException("Required input not supported" );}}}
Berechtigt
Die ermittelten Benutzerinformationen können zur Realisierung von anwendungsspezifischen Zugangsbeschränkungen oder zur Weitergabe an ein Backend-System genutzt werden. Mit JAAS können darüber hinaus diese Informationen auch für die Vergabe von Ausführungsberechtigungen genutzt werden, deren Einhaltung durch das Java-Runtime sichergestellt werden. Bereits seit dem JDK 1.2 ist die feingranulare Vergabe von Ausführungsrechten möglich. JAAS erweitert diesen Mechanismus, indem es die Kopplung von Rechten an Benutzer vorsieht. Folgendes Listing zeigt ein Beispiel, in dem Dateizugriffsberechtigungen in Abhängigkeit des ausführenden Benutzers vergeben werden:// Grant "admin" read and write access to /etc/hostsgrant Principal de.frusty.security.SamplePrinciple "admin" {permission java.io.FilePermission "/etc/hosts", "read,write";};
public class SampleAction implements PrivilegedAction{public void run(){System.out.println( "Home directory: " +System.getProperty( "user.home" ) );}}
Verteilte Konfiguration
Für den Betrieb einer JAAS-basierten Anwendung sind diverse Einstellungen durchzuführen, die in [1] und [3] ausführlich beschrieben werden. Die notwendigen Konfigurationen lassen sich in drei Bereiche unterteilen, die an unterschiedlichen Stellen im System durchzuführen sind: Allgemeine Konfigurationen, Login-Konfiguration und Policy-Konfiguration. Einstellungen zur allgemeinen Konfiguration werden in der Datei ($JRE_HOME)/lib/security/java.security vorgenommen. Hierzu gehört die Konfiguration von PolicyProvider und ConfigurationProvider. Dies sind Klassen, die für die Ermittlung und Bereitstellung von Zugriffsberechtigungen und Login-Konfiguration verantwortlich sind. JAAS stellt mit den Klassen PolicyFile und ConfigFile für beide Provider eine Default-Implementierung bereit. Beide Default-Implementierungen erwarten in java.security weitere Properties, mit denen die auszuwertenden Konfigurationsdateien spezifiziert werden [3]. Darüber hinaus ermöglichen beide Provider, durch die Spezifikation von System-Properties beim Aufruf der JVM, weitere Konfigurationsdateien zu spezifizieren. Die Login-Konfiguration legt fest, welche Login-Module für einzelne Applikationen zu verwenden sind. Folgendes Listing zeigt ein Beispiel, in dem für die Anwendung SampleApplication zwei Module konfiguriert sind:SampleApplication {de.frusty.auth.PasswordModule sufficient;de.frusty.auth.SmartCardModule required debug=true;};
Alles sicher?
Die Flexibilität und Konfigurationsmöglichkeiten von JAAS bieten breiten Raum für diverse Angriffs-Szenarien. Prinzipiell können zwei Angriffstypen unterschieden werden: das Vortäuschen falscher Identitäten durch einen Angriff auf die Login-Module und das Erlangen zusätzlicher Berechtigungen. Ersteres lässt sich zum einen durch den physischen Austausch von Login-Modulen erreichen. Durch die Verwendung von JAAS wird ein solches Vorgehen jedoch nur unwesentlich durch die Verwendung standardisierter Schnittstellen erleichtert. Da die Schnittstelle der auszutauschenden Klasse genau bekannt ist, wird die Erstellung eines gefälschten Moduls vereinfacht. Der eigentliche Austausch der Klasse lässt sich jedoch mit den gleichen Mitteln bewerkstelligen, wie auch in herkömmlichen Anwendungen. Zum anderen ist JAAS so entworfen, dass der Austausch von Login-Modulen vereinfacht wird. Durch Veränderungen an der Login-Konfiguration kann auf einfachem Weg dafür gesorgt werden, dass zur Authentisierung eines Benutzers vollkommen andere Login-Module verwendet werden. Dies kann entweder durch Editieren einer bestehenden Konfiguration oder auch durch Spezifikation einer anderen Konfiguration auf dem Weg der System-Properties (-Djava.security.auth.login.config) erfolgen, diese Möglichkeit besteht in herkömmlich realisierten Anmeldeverfahren nicht. Ein ähnlicher Weg kann auch zur Erlangung zusätzlicher Rechte genutzt werden. Auch die Dateien, in welcher die Berechtigungen konfiguriert werden, können u.U. verändert werden. Der Weg, zusätzliche Dateien auf dem Weg der System-Properties zu spezifizieren, ist ebenfalls möglich. Berechtigungen, die in einer Anwendung programmatisch vergeben werden oder unter Kontrolle eines Backend-Systems (beispielsweise einer Datenbank) stehen, sind deutlich schwieriger zu umgehen.Zur Vermeidung dieser Angriffsszenarien muss auf eine sichere Konfiguration der Systemumgebung größten Wert gelegt werden. So lassen sich Veränderungen der verwendeten Dateien durch entsprechende Dateizugriffsberechtigungen unterbinden. Durch Spezifikation der Property policy.allowSystemProperty=false in der Datei java.security lässt sich auch die Spezifikation anderer oder zusätzlicher Konfigurationsdateien auf der Kommandozeile unterbinden. Diese Maßnahmen führen jedoch nur dann zum gewünschten Ziel, wenn der Endbenutzer nicht die Berechtigung besitzt zusätzliche Software zu installieren. Besteht diese Berechtigung, dann ist ein Angreifer in der Lage ein weiteres Java-Runtime zu installieren, das seinen Bedürfnissen entsprechend konfiguriert ist. Bei Ausführung einer JAAS-Anwendung in einem solchen Java-Runtime bestehen wieder alle oben beschriebenen Angriffsmöglichkeiten. Letzteres Risiko besteht auch dadurch, dass häufig ohnehin schon mehrere Runtimes mit verschiedenen Anwendungen installiert sind. Dem kann nur dadurch Abhilfe geschaffen werden, dass ein Endbenutzer ausschließlich vordefinierte Programme starten kann. Das Ausführen von Programmen in einer Shell oder mit Start | Ausführen unter Windows muss in diesem Fall vollständig unterbunden werden.
Fazit
Vor dem Hintergrund der Wiederverwendbarkeit von Softwarekomponenten ist die mit JAAS durchgeführte Standardisierung der Schnittstellen ein sinnvoller Schritt. Auch die Möglichkeit, Anwendungen zu entwickeln, deren Benutzerauthentisierung unabhängig von der genutzten Technologie ist, bietet viel Flexibilität. Diese Unabhängigkeit verursacht jedoch die geschilderten Sicherheitsprobleme. Die für den Einsatz von JAAS benötigte Sicherheit auf Systemebene kann auf Endbenutzer-Rechnern im Allgemeinen nicht garantiert werden. Auf Server-Systemen, die nicht dem direkten Zugriff durch Endbenutzer ausgesetzt sind, ist diese Sicherheit deutlich einfacher sicherzustellen. Abschließend ist daher der Einsatz von JAAS auf Client-Systemen kritisch zu beurteilen, während er im Rahmen von Server-basierter Authentisierung (z.B. innerhalb von Servlets) durchaus zu empfehlen ist.Links und Literatur
- [1] Sun Microsystems, JAAS Developer's Guide, java.sun.com
- [2] Sun Microsystems, JAAS Login Module Developer's Guide, java.sun.com
- [3] Sun Microsystems, JAAS API Documentation, java.sun.com
- [4] Sun Microsystems, JAAS Frequently Asked Questions, java.sun.com/security/jaas/faq.html
- [5] Sun Microsystems, Security Architecture Specification, Java2 SDK Documentation, java.sun.com
- [6] Sun Microsystems, Permissions in the Java2 SDK, Java2 SDK Documentation, java.sun.com
- [7] Lai, Charlie; Samar, Vipin; Sun Microsystems, Making Login Services Independant of Authentication Technologies, java.sun.com
- [8] Lai; Gong; Koved; Nadalin; Schemers; User Authentication and Authorization in the Java (TM) Platform, Proceedings of the 15th Annual Computer Security Applications Conference, Dec. 1999




