Advanced Java Services | RMI mit SecurityManager |
Obige Beispiele sind zwar lauffähig, aber mit Vorsicht zu genießen, da keinerlei Sicherheitsvorkehrungen getroffen wurden. Server und Client sind JavaApplikation mit allen Rechten. Irgendein Client auf irgendeinem Rechner kann zu unserem Server Verbindung aufnehmen und alle Methoden des Interfaces rufen. Solange man bloß ein Datum geliefert bekommt, ist das nicht problematisch, wenn aber andere Daten geliefert werden, so kann das schon anders aussehen. Um Mißbrauch einzudämmen gibt es SecurityManager. Wird ein SecurityManager gesetzt, so überprüft er, ob für einen bestimmten Vorgang die Genehmigung erteilt worden ist. Ohne Genehmigung endet der Versuch den Vorgang auszuführen mit einer java.security.AccessControlException. Genehmigungen werden erteilt durch einen entsprechenden Eintrag in die Datei java.policy, die sich Im Verzeichnis $JAVA_HOME/jre/lib/security befindet.
// Standard extensions get all permissions by default grant codeBase "file:${java.home}/lib/ext/*" { permission java.security.AllPermission; }; // default permissions granted to all domains grant { // Allows any thread to stop itself using the java.lang.Thread.stop() // method that takes no argument. // Note that this permission is granted by default only to remain // backwards compatible. // It is strongly recommended that you either remove this permission // from this policy file or further restrict it to code sources // that you specify, because Thread.stop() is potentially unsafe. // See "http://java.sun.com/notes" for more information. permission java.lang.RuntimePermission "stopThread"; // allows anyone to listen on un-privileged ports permission java.net.SocketPermission "localhost:1024-", "listen"; // "standard" properties that can be read by anyone permission java.util.PropertyPermission "java.version", "read"; permission java.util.PropertyPermission "java.vendor", "read"; permission java.util.PropertyPermission "java.vendor.url", "read"; permission java.util.PropertyPermission "java.class.version", "read"; permission java.util.PropertyPermission "os.name", "read"; permission java.util.PropertyPermission "os.version", "read"; permission java.util.PropertyPermission "os.arch", "read"; permission java.util.PropertyPermission "file.separator", "read"; permission java.util.PropertyPermission "path.separator", "read"; permission java.util.PropertyPermission "line.separator", "read"; permission java.util.PropertyPermission "java.specification.version", "read"; permission java.util.PropertyPermission "java.specification.vendor", "read"; permission java.util.PropertyPermission "java.specification.name", "read"; permission java.util.PropertyPermission "java.vm.specification.version", "read"; permission java.util.PropertyPermission "java.vm.specification.vendor", "read"; permission java.util.PropertyPermission "java.vm.specification.name", "read"; permission java.util.PropertyPermission "java.vm.version", "read"; permission java.util.PropertyPermission "java.vm.vendor", "read"; permission java.util.PropertyPermission "java.vm.name", "read"; };
Per Default ist in einer JavaApplikation kein SecurityManager gesetzt. Die Klasse System bietet statische set/get-Methoden für einen SecurityManager an. Wir setzen den RMISecurityManager mit
System.setSecurityManager( new RMISecurityManager() ) ;
gleich am Anfang. Entsprechend gibt es eine Methode getSecurityManager(), die den gesetzten SecurityManager liefert oder null, wenn keiner gesetzt ist.
Nach dem Setzen des RMISecurityManagers endet der Versuch, den Server zu starten mit folgender Exceptionmeldung
java.security.AccessControlException: access denied (java.net.SocketPermission 192.168.45.3:1099 connect,resolve)
Wir müssen nun explizit dem Server die Erlaubnis erteilen, eine Verbindung aufzubauen. Der grant Eintrag in java.policy sieht folgendermaßen aus:
grant codeBase "file:c:/path/to/dateserver/*" { // Allow the server to connect permission java.net.SocketPermission "192.168.45.3:1024-", "connect, resolve"; };
Das allerdings reicht nicht. Denn der nächste Versuch, den server zu starten endet mit
java.rmi.UnmarshalException: Error unmarshaling return header; nested exception is: java.io.EOFException java.security.AccessControlException: access denied (java.net.SocketPermission 192.168.45.3:1036 accept,resolve)
Wir müssen dem Server nicht nur einen Verbindungsaufbau erlauben, sondern auch das Annehmen von Anfragen.
grant codeBase "file:c:/path/to/dateserver/*" { // Allow the server to connect permission java.net.SocketPermission "192.168.45.3:1024-", "accept, connect, resolve"; };
Jetzt können wir den Server starten. Allerings wird es einem Client nicht gelingen, eine Verbindung aufzubauen, er wird eine AccessControlException erhalten. Wir müssen noch zusätzlich angeben, welcher Client überhaupt berechtigt ist, eine Verbindung zum Server aufzubauen. Der vollständige grant-Eintrag sieht also wie folgt aus
grant codeBase "file:c:/path/to/dateserver/*" { // Allow the server to connect and accept permission java.net.SocketPermission "192.168.45.3:1024-", "accept, connect, resolve"; // Allow the client to connect and accept permission java.net.SocketPermission "192.168.45.1:1024-", "accept, connect, resolve"; };
1024- heißt, das für die jeweiligen IP-Adressen die Ports ab 1024 freigegeben sind. Es reicht nicht, nur den Port 1099 freizugeben.
Setzt man für den Client ebenfalls einen RMISecurityManager, so muß man dem Client explizit erlauben, eine Verbindung zum Server aufzubauen. Der Eintrag in java.policy sieht folgendermaßen aus.
grant codeBase "file:c:/path/to/dateclient/*" { // Allow the client to connect to the server , server = 192.168.45.3 // connect schließt resolve ein, client braucht nur connect permission java.net.SocketPermission "192.168.45.3:1024-", "connect"; };
Ein Applet kann nur Kontakt zu dem Server aufnehmen, von dem es geladen worden ist. Dies stellt ein spezieller SecurityManager für Applets sicher, der für jedes Applet per default verwendet wird. Verwendet der Browser das plug-in von SUN, so ist das sun.plugin.security.ActivatorSecurityManager.
Wenn der Server keinen SecurityManager aktiviert, kann ein Applet ohne weiteres Kontakt zum DateServer aufnehmen, egal von wo aus es das tut, wichtig ist nur, daß es von dem Rechner stammt zu dem es Kontakt aufnehmen will.
Auch in diesem Fall kann der Client ein Applet sein. Da das Applet aber lokal auf dem Client läuft wird bei einem Verbindungsaufbau zum Server die lokale IP-Adresse übermittelt und das heißt, daß serverseitig in der java.policy explizit die IP-Adressen stehen müssen, für die der Zugriff erlaubt ist. Für dynamische Adressen ist somit kein Zugriff möglich.