Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage |
<< | < | > | >> | API | Kapitel 47 - Sicherheit und Kryptographie |
In diesem Abschnitt wird beschrieben, wie einem Applet der Zugriff auf geschützte Ressourcen gewährt werden kann. Wir gehen dazu in folgenden Schritten vor:
Wir wollen uns die Aufgabe stellen, ein Applet zu schreiben, das einige Sicherheitsverstöße begeht. Zunächst soll es eine Datei auf dem lokalen Computer erzeugen und einen Zeitstempel hineinschreiben. Zusätzlich soll es auf einige geschützte System-Properties zugreifen und deren Inhalt in die Datei schreiben. Das Applet hat folgenden Aufbau:
001 /* TrustedApplet.java */ 002 003 import java.awt.*; 004 import java.applet.*; 005 import java.util.*; 006 import java.io.*; 007 008 public class TrustedApplet 009 extends Applet 010 { 011 static final String ALLOWED_DIR = "c:\\tmp\\applets\\"; 012 static final String FNAME = "TrustedApplet.log"; 013 static final String LOGMSG = "Erzeugt von Applet: "; 014 String msg; 015 016 public void init() 017 { 018 msg = "Uninitialisiert"; 019 FileWriter out = null; 020 try { 021 //Ausgabedatei erzeugen 022 out = new FileWriter(ALLOWED_DIR + FNAME); 023 //Logmessage schreiben 024 out.write(LOGMSG); 025 //Zeitstempel schreiben 026 GregorianCalendar cal = new GregorianCalendar(); 027 out.write(cal.get(Calendar.DATE) + "."); 028 out.write((cal.get(Calendar.MONTH) + 1) + "."); 029 out.write(cal.get(Calendar.YEAR) + " "); 030 out.write(cal.get(Calendar.HOUR_OF_DAY) + ":"); 031 out.write(cal.get(Calendar.MINUTE) + ":"); 032 out.write(cal.get(Calendar.SECOND) + ""); 033 out.write(System.getProperty("line.separator")); 034 //System-Properties lesen und in Datei schreiben 035 out.write(getProp("user.name")); 036 out.write(getProp("user.home")); 037 out.write(getProp("user.dir")); 038 //Datei schließen 039 msg = "Alle Sicherheitshuerden ueberwunden!"; 040 } catch (Exception e) { 041 msg = e.toString(); 042 } finally { 043 if (out != null) { 044 try { 045 out.close(); 046 } catch (IOException e) { 047 //silently ignore 048 } 049 } 050 } 051 } 052 053 public void paint(Graphics g) 054 { 055 g.drawString(msg, 20, 20); 056 } 057 058 private String getProp(String prop) 059 { 060 return prop + "=" + 061 System.getProperty(prop) + 062 System.getProperty("line.separator"); 063 } 064 } |
TrustedApplet.java |
Es versucht zunächst eine Datei c:\tmp\applets\TrustedApplet.log zu erzeugen. Wenn dies gelingt, instanziert es ein aktuelles GregorianCalendar-Objekt und schreibt dessen Werte in die Datei. Schließlich versucht es, die System-Properties "user.name", "user.home" und "user.dir" zu lesen und deren Werte ebenfalls in die Datei zu schreiben. Sind all diese Versuche erfolgreich, gibt das Applet die Meldung "Alle Sicherheitshuerden ueberwunden!" aus. Tritt eine Ausnahme auf, wird deren Text ausgegeben.
Um das Schreiben der Datei zu ermöglichen, ist ein Verzeichnis c:\tmp\applets anzulegen. Andernfalls gibt es eine IOException - selbst wenn alle Sicherheitshürden genommen sind. |
|
Das Applet kann mit folgender HTML-Datei gestartet werden:
001 <html> 002 <head> 003 <title>TrustedApplet Demo</title> 004 </head> 005 006 <body> 007 <h1>TrustedApplet Demo</h1> 008 009 <applet 010 code="TrustedApplet.class" 011 width=600 012 height=200 013 > 014 TrustedApplet Demo 015 </applet> 016 017 </body> 018 </html> |
Das Applet kann nun beispielsweise mit dem appletviewer
gestartet werden:
appletviewer TrustedApplet.html
Wird es ohne weitere Vorkehrungen gestartet, scheitert es bereits am Erzeugen der Ausgabedatei und gibt eine SecurityException auf dem Bildschirm aus.
Zum Signieren ist es zunächst erforderlich, alle für den
Betrieb des Applets nötigen Dateien in ein jar-File zu packen.
Signiert wird also nicht eine einzelne .class-Datei,
sondern alle Dateien innerhalb des jar-Archivs. Dazu verwenden wir
folgendes Kommando:
jar cvf trapp.jar TrustedApplet.class
Jetzt wird ein jar-Archiv trapp.jar erstellt, das die Klassendatei TrustedApplet.class enthält. Dieses Archiv muß nun signiert werden. Dazu steht im JDK das Hilfsprogramm jarsigner zur Verfügung. Es arbeitet kommandozeilenbasiert und wird folgendermaßen aufgerufen:
jarsigner -signedjar outjar injar alias |
Die einzelnen Elemente haben folgende Bedeutung:
Nachdem wir in Abschnitt 47.1.6
bereits ein Schlüsselpaar mit dem Alias "hjp3" erzeugt und in
der Schlüsseldatenbank abgelegt haben, muß der Aufruf von
jarsigner
so erfolgen:
C:\--->jarsigner -signedjar strapp.jar trapp.jar hjp3
Enter Passphrase for keystore: hjp3ks
Enter key password for hjp3: hjp3key
Die beiden Paßwörter zum Zugriff auf die Schlüsseldatenbank und den Schlüssel werden auf Nachfrage in der Kommandozeile angegeben. Nachdem das Programm einige Zeit gerechnet hat, erzeugt es das signierte Archiv mit dem Namen strapp.jar.
jarsigner bietet neben den hier erwähnten noch weitere Optionen, auf die wir nicht weiter eingehen wollen. Das Programm wird zusammen mit den anderen Hilfsprogrammen in der Tool-Dokumentation des JDK ausführlich beschrieben. |
|
Bisher wurde das Applet direkt aus der .class-Datei geladen. Um es aus einem jar-Archiv zu laden, muß das APPLET-Tag der HTML-Datei um das archive-Argument erweitert werden:
001 <-- TrustedApplet.html --> 002 003 <html> 004 <head> 005 <title>TrustedApplet Demo</title> 006 </head> 007 008 <body> 009 <h1>TrustedApplet Demo</h1> 010 011 <applet 012 archive="strapp.jar" 013 code="TrustedApplet.class" 014 width=600 015 height=200 016 > 017 TrustedApplet Demo 018 </applet> 019 020 </body> 021 </html> |
TrustedApplet.html |
Wir könnten das Applet jetzt wie zuvor starten, würden aber immer noch dieselbe SecurityException erhalten. Es ist zwar signiert und das Zertifikat ist auf diesem Rechner bekannt (denn hier wurde es erstellt). Die Policy-Datei ist aber noch nicht angepaßt, und daher lehnt der SecurityManager des JDK die Ausführung der gefährlichen Operationen nach wie vor ab. |
|
Soll das signierte Applet auf anderen Arbeitsplätzen laufen, ist es erforderlich, das Zertifikat des Schlüssels, mit dem es signiert wurde, dort zu installieren. Soll es dagegen nur auf dem Arbeitsplatz laufen, auf dem auch der Schlüssel generiert wurde, ist das nicht erforderlich. Bei der Generierung des Schlüssels wurde ja auch ein (selbstsigniertes) Zertifikat erzeugt.
Um das Zertifikat weitergeben zu können, muß es zunächst
unter Verwendung der Option -export
von keytool
aus der lokalen Schlüsseldatenbank exportiert werden:
keytool -export -alias hjp3 -file hjp3.cert
Es liegt nun in der Datei hjp3.cert und
kann auf das Zielsystem kopiert werden. Mit der -import-Option
von keytool
kann es dort in die Schlüsseldatenbank aufgenommen werden:
C:\--->keytool -import -alias hjp3 -file hjp3.cert
Enter keystore password: hjp3ks
Owner: CN=Guido Krueger, O=Computer Books, C=de
Issuer: CN=Guido Krueger, O=Computer Books, C=de
Serial number: 38663e2d
Valid from: Sun Dec 26 17:11:25 GMT+01:00 1999 until: Sat Mar 25 17:11:25 GMT+01
:00 2000
Certificate fingerprints:
MD5: D5:73:AB:06:25:16:7F:36:27:DF:CF:9D:C9:DE:AD:35
SHA1: E0:A4:39:65:60:06:48:61:82:5E:8C:47:8A:2B:04:A4:6D:43:56:05
Trust this certificate? [no]: y
Certificate was added to keystore
Nach dem Aufruf muß zunächst das Paßwort der Schlüsseldatenbank angegeben werden. Dann zeigt das Programm die Eigenschaften des Zertifikats an und erwartet, daß die Informationen bestätigt werden. Anschließend wird das Zertifikat in die Schlüsseldatenbank aufgenommen.
Der letzte Schritt besteht darin, die Sicherheitseinstellungen auf dem Zielsystem anzupassen. Applets, die mit dem Zertifikat verifiziert werden können, das unter dem Alias "hjp3" in der Schlüsseldatenbank abgelegt wurde, sollen Dateien im Verzeichnis c:\tmp\applets lesen und schreiben und auf die System-Properties "user.name", "user.home" und "user.dir" zugreifen können.
Die Sicherheitseinstellungen des JDK werden mit Hilfe von Policy-Dateien definiert. Es gibt zwei Stellen im Dateisystem, von denen das JDK sie standardmäßig einliest:
Policy-Dateien können auch an beliebigen anderen Stellen im Dateisystem liegen. In diesem Fall muß beim Aufruf des Java-Interpreters das System-Property "java.security.policy" mit dem Namen der zu verwendenen Policy-Datei angegeben werden. Wäre beispielsweise hjp3policy die zu verwendende Policy-Datei, so müßte der Appletviewer mit der Option "-J-Djava.security.policy=hjp3policy" aufgerufen werden. |
|
Policy-Dateien sind zeilenorientierte Textdateien, die mit einem gewöhnlichen Texteditor bearbeitet werden können. Alternativ stellt das JDK ein einfaches GUI-basiertes Hilfsprogramm mit dem Namen policytool zur Verfügung, mit dem Policy-Dateien erstellt und bearbeitet werden können. Auf seine Verwendung wollen wir aber nicht weiter eingehen.
Eine Policy-Datei enthält zwei Arten von Einträgen. Beide sind optional:
Alle Einträge werden mit einem Semikolon beendet. Kommentare können an beliebiger Stelle durch // oder /* ... */ angelegt werden.
Der "keystore"-Eintrag erwartet als Argument einen URL, der auf die
Schlüsseldatenbank verweist. Auf einem Windows-98-Einzelplatzsystem
sieht er beispielsweise so aus:
keystore "file:/c:/windows/.keystore";
Die "grant"-Einträge haben folgende Syntax:
grant [SignedBy "signer"] [, CodeBase "URL"] { permission permission_class [ "target" ] [, "action"] [, SignedBy "signers"]; ... }; |
Eine Berechtigung kann wahlweise an einen Unterzeichner oder eine Ladeadresse (oder beide) vergeben werden. Die Option "SignedBy" führt eine Liste von Aliasnamen auf, deren Zertifikate vorhanden sein müssen, damit die Berechtigung gewährt wird. Die Option "CodeBase" spezifiziert die Adresse, von der ein Applet geladen werden darf, um die Berechtigung zu halten. Fehlt die CodeBase-Klausel, wird nur die Unterzeichnerliste verwendet; fehlt die SignedBy-Klausel, ist es nur die Ladeadresse.
Nach einer öffnenden geschweiften Klammer folgen beliebig viele Berechtigungen, die jeweils durch das Schlüsselwort "permission" eingeleitet werden. Anschließend wird der Eintrag mit einer schließenden geschweiften Klammer abgeschlossen. Der zuvor spezifizierte Berechtigte erhält alle Berechtigungen, die zwischen den beiden Klammern angegeben sind.
Jede Berechtigung muß als erstes Argument den Namen einer Berechtigungsklasse angeben. Daneben kann sie zwei weitere Argumente target und action haben, mit denen Details spezifiziert werden. Tabelle 47.1 listet die gebräuchlichsten Berechtigungen und ihre Argumente auf. Details können (und müssen) in dem Dokument "Java Security Architecture" nachgelesen werden, das Bestandteil der JDK-Dokumentation ist.
Klasse | Zugriff auf | Target | Action |
java.io.FilePermission | Dateien und Verzeichnisse | Datei- oder Verzeichnisnamen. Wird als letztes Zeichen ein "*" angegeben, so gilt die Berechtigung für das komplette Verzeichnis. Steht dort ein "-", so gilt sie zusätzlich für alle Unterverzeichnisse. Wird "<<ALL FILES>>" angegeben, gilt die Berechtigung für alle Dateien in allen Verzeichnissen! | read, write, delete, execute |
java.net.SocketPermission | TCP/IP-Verbindungen | Hostname oder IP-Adresse, gefolgt von Portnummern-Bereich | accept, connect, listen, resolve |
java.util.PropertyPermission | System-Properties | Names des Properties | read, write |
java.lang.RuntimePermission | Die Klasse Runtime | exitVM, stopThread, loadLibrary, queuePrintJob, ... | - |
java.awt.AWTPermission | Window-Ressourcen | accessClipboard, showWindowWithoutWarningBanner, ... | - |
java.security.AllPermission | Alle Ressourcen | - | - |
Tabelle 47.1: Wichtige Permission-Klassen
Nach diesen Vorbemerkungen können wir die Policy-Datei \windows\.java.policy
erstellen. Sie hat folgenden Inhalt:
keystore "file:/c:/windows/.keystore";
grant SignedBy "hjp3" {
permission java.io.FilePermission "c:\\tmp\\applets\\*", "read,write";
permission java.util.PropertyPermission "user.name", "read";
permission java.util.PropertyPermission "user.home", "read";
permission java.util.PropertyPermission "user.dir", "read";
};
Im ersten Eintrag geben wir die Position der Schlüsseldatenbank an, sie liegt im Verzeichnis c:\windows. Anschließend definieren wir die Berechtigungen für alle Applets, die mit dem Zertifikat "hjp3" signiert wurden. Zunächst erhalten sie Schreib- und Leseberechtigung im Verzeichnis c:\tmp\applets. Dort können sie ohne weitere Einschränkungen Dateien anlegen, überschreiben und lesen. Zusätzlich erlauben wir den so signierten Applets, die drei System-Properties "user.name", "user.home" und "user.dir" zu lesen.
Nun läßt sich unser signiertes Applet ohne SecurityException
aufrufen und gibt die erlösende Meldung "Alle Sicherheitshuerden
ueberwunden" aus. Im Verzeichnis c:\tmp\applets
sollte sich anschließend eine Datei TrustedApplet.log
befinden und etwa folgenden Inhalt haben:
Erzeugt von Applet: 30.12.1999 20:50:40
user.name=Guido Krüger
user.home=C:\WINDOWS
user.dir=C:\arc\doku\hjp3\misc
Die Prüfung der Zugriffsberechtigungen wird mit Hilfe der Klasse SecurityManager aus dem Paket java.lang vorgenommen. Der SecurityManager ist ein Objekt, das entweder gar nicht oder genau einmal im laufenden Java-Programm vorhanden ist. Nach der ersten Instanzierung kann es nicht mehr geändert oder entfernt werden.
Zugriffe auf den SecurityManager
sind an den Stellen der Laufzeitbibliothek eingebaut, an denen auf
gefährliche Ressourcen zugegriffen wird. Ein Beispiel aus der
Klasse FileInputStream
sieht etwa so aus:
...
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(name);
}
//Gefährlicher Code
...
Zunächst wird geprüft, ob ein SecurityManager installiert wurde. Ist das nicht der Fall, fährt das Programm ohne Einschränkung fort. Gibt es dagegen einen SecurityManager, wird dessen checkRead-Methode aufgerufen. Sie löst eine SecurityException aus, wenn die gewünschte Berechtigung fehlt. Der Code hinter dem checkRead wird in diesem Fall nicht mehr erreicht. Ist die Berechtigung dagegen vorhanden, wird checkRead ohne weitere Aktionen beendet und der dahinter liegende Code ausgeführt.
Applets besitzen grundsätzlich einen SecurityManager.
Der AppletViewer bzw. Web-Browser sorgen während der Initialisierung
für dessen Instanzierung. Applikationen dagegen haben normalerweise
keinen SecurityManager
(das ist der Grund, weshalb in Applikationen alle gefährlichen
Operationen erlaubt sind). Soll eine Applikation einen SecurityManager
erhalten, so kann sie entweder mit der Option "-Djava.security.manager"
gestartet werden, oder der SecurityManager
kann im Programm selbst installiert werden:
...
System.setSecurityManager(new SecurityManager());
...
Das folgende Beispielprogramm gibt den Inhalt des System-Properties "user.name" aus. Normalerweise kann es ohne Fehler ausgeführt werden:
001 /* SecuMgrTest.java */ 002 003 public class SecuMgrTest 004 { 005 public static void main(String[] args) 006 { 007 System.out.println( 008 "user.name is " + System.getProperty("user.name") 009 ); 010 } 011 } |
SecuMgrTest.java |
Läuft es dagegen unter Kontrolle eines SecurityManagers, so führt
der Aufruf zu einer SecurityException:
C:\--->java -Djava.security.manager SecuMgrTest
Exception in thread "main"
java.security.AccessControlException: access denied
(java.util.PropertyPermission user.name read)
at java.security.AccessControlContext.checkPermission(
AccessControlContext.java:276)
at java.security.AccessController.checkPermission(
AccessController.java:403)
at java.lang.SecurityManager.checkPermission(
SecurityManager.java:549)
at java.lang.SecurityManager.checkPropertyAccess(
SecurityManager.java:1242)
at java.lang.System.getProperty(System.java:555)
at Test1.main(SecuMgrTest.java:7)
Bei Bedarf kann man Applikationen auf diese Weise mit denselben Sicherheitsmechanismen ausstatten wie Applets. jar-Dateien, aus denen Applikationen geladen werden, lassen sich ebenso signieren wie solche, aus denen Applets geladen werden. Die in der Policy-Datei definierten Rechte gelten dann für die daraus gestartete Applikation.
Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage, Addison Wesley, Version 3.0.1 |
<< | < | > | >> | API | © 1998-2003 Guido Krüger, http://www.javabuch.de |