MokaByte Numero 17 - Marzo 1998
Foto

 
JDK 1.2: Modello di Sicurezza 
di
Fausto 
Fraticelli
Analisi dei cambiamenti apportati al modello di sicurezza di Java




Il JDK 1.2 presenta un nuovo modello di sicurezza che rimane compatibile con le precedenti versioni, ma ne estende notevolmente le potenzialita'. I vantaggi piu' evidenti che ne derivano sono: amministrazione semplificata dei privilegi delle applet; configurazione dei privilegi anche per le applicazioni stand-alone; possibilita' di rilasciare i privilegi anche ai codici non firmati; creazione dei nuovi privilegi in maniera semplice e trasparente.



 

Introduzione

Sandbox
La struttura classica della sicurezza di Java punta principalmente a prevenire attacchi da parte di applet caricate dalla rete nel sistema locale (vedi figura 2). Il modello e' il sandbox . Con tale termine vengono incluse una serie di strumenti che agiscono in fase di caricamento (bytecode verifier) e d’esecuzione (security manager), che hanno il compito di controllare che il codice dell’applet caricata rispetti le regole semantiche del linguaggio Java ed evitare che l’applet usi indiscriminatamente le risorse critiche del sistema locale. Alcune delle operazioni potenzialmente pericolose e necessariamente da proteggere sono: lettura/scrittura su file system, connessioni ad host diversi da quello da cui provengono, utilizzare procedure native, eseguire comandi della classe java.lang.Runtime come l’exec e l’exit, etc..


Figura 1

JDK 1.1.x
Il secondo passo nelle versioni successive del JDK (dalla 1.1) e’ stato quello di aumentare i privilegi delle applet esterne considerate "fidate". Per tale scopo sono stati inseriti nuovi packages riguardanti strumenti di crittografia, digesting, firme digitali (digital signing), certificazione e liste di controllo degli accessi (ACL) alle risorse.
Inoltre sono stati introdotti alcuni tools (javakey, jar) per gestire databases locali contenenti entità autenticabili  e relative chiavi e per creare i JAR files contenenti le applet firmate.
Anche se sono stati compiuti notevoli passi in avanti, rimane comunque complessa la gestione dei privilegi d'accesso e la configurazione di una security policy. Inoltre i tools possono essere utilizzati per aumentare i privilegi solo a quelle applet che vengono autenticate in base alla firma che le accompagna.
 

Sicurezza nel JDK 1.2
Il modello di sicurezza del JDK 1.2 presenta aspetti del tutto nuovi che fanno riferimento ai seguenti concetti e strumenti:

. domini;
. security policy;
. permessi;
. AccessController.

Domini
La versione 1.2 del JDK (release beta 2) introduce una nuova struttura nel modello di sicurezza basato sul concetto dei domini di protezione. Un dominio di protezione e' un insieme di privilegi (o permessi) associati ad un insieme di codici Java (applet o applicazioni) aventi le seguenti caratteristiche comuni:

- provenienza da uno specifico CODEBASE (es: "http://www.serv_host.com/", "*", "file:///C|/.../index.html");
- firmate usando tutte le firme digitali appartenenti ad uno specifico insieme di firme.

Quindi un dominio di protezione (o dominio) puo' esser visto intuitivamente come un insieme di applet e/o applicazioni Java raggruppate in base alla loro provenienza (di tipo java.net.URL) e alle chiavi pubbliche (rappresentata da oggetti di tipo java.security.PublicKey) utilizzate per verificare le loro firme. Il risultato e' che il codice Java viene caratterizzato dalla sua origine (URL) e dall'insieme di chiavi che lo accompagna. Nel resto di questo articolo quando diremo che un codice Java appartiene ad un dominio si vuole intendere che quel codice possiede le proprieta' (URL, chiavi) associate a quel dominio.
Domini speciali sono:

- il dominio di sistema, si riferisce a tutto il codice Java incluso nel CLASSPATH del sistema locale.
- il sandbox, al quale appartengono i codici Java che non hanno privilegi speciali nell'host locale;

Nella coppia (URL, chiavi) che caratterizza il dominio di sistema, la URL corrisponde al CLASSPATH  di sistema e l'insieme di chiavi pubbliche e' vuoto, ossia il codice derivante dal CLASSPATH non necessita di essere autenticato.
La coppia (URL, chiavi) che si riferisce al sandbox (dominio che potrebbe anche non esistere qualora si rilasciassero dei privilegi a tutta l'utenza di Internet), include tutte le coppie (URL, chiavi) non privilegiate nel dominio di sistema (nel seguito analizzeremo meglio questi concetti).
Ogni codice Java (applet o applicazione) e' associato ad uno specifico dominio a seconda della sua provenienza e delle firme che porta con se'; inoltre, per poter accedere alle risorse protette dal dominio di sistema (gestione di rete, file system, etc..) deve avere permessi specifici di questo dominio. Nel caso in cui un’applicazione non avesse il permesso di utilizzare risorse associate ad un dominio, viene rilevata una SecurityException.
Il fatto che il dominio si basi sulla coppia (URL, chiavi) permette di definire una specifica politica di sicurezza anche per le applicazioni stand-alone. Infatti una URL puo' essere di tipo file:///C|/.../index.html e la firma digitale e' indipendente dalla provenienza del codice.
Quindi ogni dominio puo' avere delle risorse da proteggere attraverso degli accessi controllati e puo' avere lui stesso dei permessi su altri domini. Nell'ambito di questo articolo ci interessiamo solo di come il dominio di sistema protegge l'accesso alle risorse di sistema e di come l'utente possa configurare una propria politica di sicurezza. Nella figura 1 e' mostrato un'esempio di come si possono gestire i permessi del dominio di sistema a seconda dei domini di appartenenza dei codici.


Figura 2

In questo esempio il dominio di sistema rilascia il permesso di eseguire il metodo exec e di connettersi a host diversi da quello di origine al dominio D0; al dominio D1 da' i privilegi di lettura/scrittura sulla directory /public. Nel seguito vedremo come fa a rilasciare i permessi del dominio di sistema ai domini non di sistema (application domain), ossia ai vari codici non interni al CLASSPATH.

Policy e Permission
E' possibile definire i permessi sugli oggetti del dominio di sistema e conservarli in un'oggetto chiamato Policy. Una policy e’ semplicemente una lista di permessi rilasciati ai codici a seconda della loro provenienza o delle loro firme. Il fine e’ permettere ad un utente di incrementare o negare i permessi di sistema, in maniera semplice, ad un’entità locale o remota (URL, utenti firmati, tutti gli utenti di Internet, etc..).
Vediamo un esempio di policy:

grant {
  permission java.io.FilePermission "/tmp/readme.txt", "read";
};
grant signedBy "Tizio,Caio" {
  permission java.io.FilePermission "/tmp", "read";
};
grant CodeBase "http://myhost.it/", signedBy "Tizio" {
  permission java.io.FilePermission "/tmp", "read";
  permission java.net.SocketPermission "*", "connect";
};
Un blocco grant rilascia dei permessi specifici ai codici che provengano dalle URL (opzionali) e dai firmatari (opzionali) che seguono il grant. Il primo blocco dell'esempio rilascia un permesso a tutti gli utenti di Internet, il secondo rilascia un permesso ai codici firmati da Tizio e Caio ed il terzo si riferisce ai codici provenienti da http://myhost.it e firmati da Tizio. Il blocco seguente:
grant CodeBase "http://myhost.it/", signedBy "Tizio" {
  permission java.io.FilePermission "/tmp/readme.txt","read";
}
non fa altro che caricare nel Policy un permesso. Questo permesso si riferisce alla possibilita' da parte dei codici provenienti da http://myhost.it/ e firmati da Tizio di leggere il file readme.txt della directory /tmp.
I permessi nel dominio di sistema del JDK 1.2 sono definiti nelle seguenti classi:
· java.io.FilePermission;
· java.net.SocketPermission;
· java.util.PropertyPermission;
· java.lang.RuntimePermission;
· java.awt.AWTPermission;
· java.net.NetPermission.

Ad esempio tramite il SocketPermission si possono rilasciare permessi a codici interni e/o esterni per connettersi ad altri host; con il RuntimePermission si puo' dare il permesso di eseguire un exec o un exit, etc..
La Policy (contenente l'insieme di permessi rilasciati ai codici autorizzati) viene caricata nel momento in cui si inizializza la macchina virtuale di Java (JVM). Nel caso in cui la Policy sia vuota, allora e' come se si applicasse il modello sandbox per tutti i codici esterni al CLASSPATH. La Policy puo' essere configurata e/o modificata anche dinamicamente, ossia dopo che sia stata gia' inizializzata. Puo' essere istanziata una sola Policy nella JVM, ossia non possono essere caricate contemporaneamente due Policy.

AccessController
Supponiamo che un'applicazione faccia una chiamata ad un metodo protetto (M) del dominio di sistema. Il metodo prima di svolgere il proprio compito chiama (direttamente o indirettamente) l'oggetto AccessController per verificare che l'applicazione (appartenente ad un determinato dominio) abbia il permesso di invocare il metodo. L'AccessController reperisce le informazioni sui permessi che il dominio dell'applicazione possiede sul dominio di sistema. Se tra questi permessi e' presente quello di utilizzare il metodo M, l'AccessController non fa altro che ritornare al metodo, che eseguira' normalmente l'operazione. In caso contrario l'AccessController riportera' una SecurityException (anzi, una java.security.AccessControlException) e l'operazione non verra' eseguita.
Quindi l'oggetto AccessController si occupa di verificare che l'applicazione abbia i giusti permessi sul metodo chiamato, similmente a quanto faceva il SecurityManager con i check. Pero' l'AccessController non esegue un semplice check.
Innanzitutto verifica a quale contesto appartiene l'applicazione. Poi si fa dare dall'oggetto Policy la lista di permessi rilasciati a quel contesto. Infine verifica che in questa lista di permessi ci sia quello di utilizzare il metodo M.
 

Configurazione di una propria Security Policy
In questa sezione descriviamo come si puo' configurare una propria politica di sicurezza. Cio' ci permettera' di approfondire anche i meccanismi di verifica ed autenticazione dei codici firmati. In questo esempio ci mettiamo prima dalla parte del creatore (chiamiamolo Fido) e firmatario di una applet che voglia eseguire operazioni privilegiate in un normale client host. In seguito vedremo come un client (chiamiamolo Diffido) debba settare la sua policy per autenticare i codici appartenenti a Fido e per dare loro i privilegi necessari.

Creazione chiavi
Innanzitutto Fido deve avere una coppia di chiavi (pubblica e privata). Con la chiave privata firma i file da mandare. Diffido deve avere una copia della chiave pubblica di Fido per poter verificare la firma di Fido. Per creare una coppia di chiavi Fido eseguira’ il seguente comando:

keytool -genkey -alias Fido -keypass fido@16.1.98/10,30h
con il quale crea un alias (Fido) e inserisce una password (fido@16.1.98/10,30h) per usare la propria chiave privata. Il comando keytool memorizzerà le chiavi e gli eventuali certificati in un oggetto chiamato keystore (implementazione della Sun della classe astratta java.security.KeyStore), memorizzandolo in un file e proteggendolo adeguatamente con una password. Dopo la richiesta della password usata per proteggere il keystore creato (nell’esempio ho usato fidonew) e l’inserimento di alcune informazioni personali (nome e cognome, società, città, etc..) verranno create le chiavi da usare per firmare i propri codici e farli autenticare. Il keystore verra’ salvato come file .keystore nella home directory dell’utente (supponiamo in /).

Firma dell'applet
A questo punto Fido dovra’ creare un file .jar (chiamiamolo FidoSignedApplet.jar) contenente i file .class dell’applet (supponiamo che l’applet sia un unico file: FidoApplet.class). Per creare questo file basta eseguire la seguente linea di comando:

jar cf FidoSignedApplet.jar FidoApplet.class
Ora bisogna che Fido firmi la sua applet (contenuta nel .jar file) con la linea di comando:
jarsigner -keystore /.keystore -storepass fidonew -keypass fido@16.1.98/10,30h FidoSignedApplet.jar Fido
Questa istruzione genera il file FidoSignedApplet.jar derivante dall’aggiunta dei file fido.sf e fido.dsa al precedente FidoSignedApplet.jar. Si noti l’uso delle password (-storepass fidonew .. -keypass fido@16.1.98/10,30h) per accedere al keystore di Fido e alla chiave privata memorizzata all’interno del keystore. Come risultato FidoSignedApplet.jar conterra' oltre a FidoApplet.class i file manifest.mf, fido.sf e fido.dsa che servono a Diffido per autenticare Fido e per verificare l'integrita' dell'applet caricata.

Tag archive
Ora basta che Fido prepari una pagina HTML (FidoApplet.html) con il tag seguente:

<applet code=FidoApplet.class archive="FidoSignedApplet.jar" width=100 height=100> </applet>
Download della chiave pubblica
Vediamo cosa deve fare Diffido per dare privilegi maggiori ai codici di Fido in maniera "sicura". Per prima cosa deve avere la chiave pubblica di Fido per verificarne la firma. Questa copia sarà tenuta in un database (consultato dalla JVM quando si carica un’applet) insieme all’alias di Fido. Con il seguente comando Fido memorizza la sua chiave pubblica in un file da inviare (con e-mail, FTP, etc..) a Diffido:
keytool -export -alias Fido -file Fido.cert
Una volta ottenuto il file Fido.cert Diffido lo importa nel suo database associandolo ad un alias per Fido (NickFido) con l’istruzione seguente:
keytool -import -alias NickFido -file Fido.cert
Questo comando crea un nuovo record nel file .keystore (di Diffido) con alias NickFido, in cui si memorizza la chiave pubblica di Fido (necessaria per verificare la sua firma) e si indica se questa e’ stata certificata. E’ l’utente locale stesso che dovra’ indicare se la chiave può essere considerata sufficientemente certificata. Finora abbiamo creato le basi per permettere a Diffido di autenticare i codici di Fido.

Gestione del Policy
Ora vediamo come sia possibile gestire i permessi su questi codici. Volendo rilasciare piu' privilegi nelle operazioni sul file system, Diffido potrebbe aggiungere il seguente grant nel file di configurazione personale (ad es. DiffidoPolicy.txt):

grant signedBy "NickFido" {
 permission java.io.FilePermission "*", "read";
 permission java.io.FilePermission "/home_Diffido/*", "read,write";
};
Questo grant rilascia i privilegi d'accesso in lettura su tutto il file system di Diffido e in scrittura sulla directory /home_Diffido ai codici firmati da Fido (che nel sistema di Diffido ha l'alias "NickFido") senza preoccuparsi della loro provenienza.

Appletviewer
Per impostare il proprio file policy quando si usa l’appletviewer basta associare -Djava.policy al nome del file. Ad esempio caricando l’applet di Fido dal suo sito con il comando seguente:

appletviewer -J-Djava.policy=/home_Diffido/DiffidoPolicy.txt http://www.fidohost.it/FidoApplet.html

si abilita l’applet a scrivere nella directory /home_Diffido e a leggere tutti i file del file system locale. Infine e' previsto l'utilizzo dell'opzione -Djava.policy per settare una propria policy anche per le applicazioni stand-alone.
 

Conclusioni
In questo articolo si e' visto come il modello di sicurezza del JDK 1.2 permetta di gestire in maniera semplice le liste di accesso alle risorse locali, di applicarle alle applicazioni stand-alone e di svincolarle dall’autenticazione tramite firme digitali.
L’analisi fatta non puo’ essere esauriente finche’ non si verificano i cambiamenti apportati alla JVM del JDK su un Java-enhanced browser. L’appletviewer del JDK 1.2 per quanto sia utilizzabile per testare il nuovo modello, non può essere considerato un banco di prova affidabile, infatti molte delle operazioni normalmente protette dal sandbox nei normali Java-enhanced browser non sono protette dall'appletviewer.

  
 
 
 
 

 

MokaByte rivista web su Java

MokaByte ricerca nuovi collaboratori
Chi volesse mettersi in contatto con noi può farlo scrivendo a mokainfo@mokabyte.it