2009-09-14 8 views
15

Nel mio web.xml ho definito un utente-dati-vincolo per alcune risorse:Come prevenire il dirottamento della sessione Tomcat?

<security-constraint> 
    <web-resource-collection> 
     <web-resource-name>Personal Area</web-resource-name> 
     <url-pattern>/personal/*</url-pattern> 
    </web-resource-collection> 
    <web-resource-collection> 
     <web-resource-name>User Area</web-resource-name> 
     <url-pattern>/user/*</url-pattern> 
    </web-resource-collection> 
    <user-data-constraint> 
     <transport-guarantee>CONFIDENTIAL</transport-guarantee> 
    </user-data-constraint> 
</security-constraint> 
  1. Quando carico la pagina con http ho ottenuto il mio JSESSIONID ID1 nel mio biscotto.
  2. Quando si passa a context/user/sample.faces, Tomcat esegue un reindirizzamento 302 su HTTPS. Ma il mio JSESSIONID è ancora ID1.

Penso che questa sia una vulnerabilità? O è il mio errore di configurazione?

Il problema che vedo è il seguente: Durante la navigazione su HTTP con cookie ID1 c'è un utente malintenzionato che sta ascoltando il mio traffico di rete. Lui "ruba" il mio cookie ID1. Ora passo a HTTPS e il mio cookie è ancora ID1. Accedo L'attaccante è quindi in grado di prendersi cura della mia sessione perché conosce il mio cookie ...

+0

Per il tuo commento: Il motivo per cui lo SSLID è lo stesso è che la sessione è la stessa (ho appena fatto clic su Aggiorna in Firefox, dopo tutto). Puoi usare questo fatto nella gestione della sessione. Per quanto riguarda il modo in cui è stato creato lo SSLID, non è coperto dalle specifiche del servlet in modo che ogni fornitore possa utilizzare i propri meccanismi. Dovresti controllare le fonti Tomcat, mi aspetto. Ad ogni modo, non dovresti fare affidamento su alcuna implementazione specifica - usala come useresti JSESSIONID - come un valore opaco. –

risposta

11

Se si tratta di una versione recente di Tomcat, potrebbe non esserci un problema. Tuttavia, ciò dipende dal controllo dell'ID SSL associato alla sessione. Questo è disponibile utilizzando il codice come

String sslId = (String) req.getAttribute("javax.servlet.request.ssl_session"); 

(Si noti che la chiave di attributo può cambiare in futuro per javax.servlet.request.ssl_session_id - come parte delle specifiche Servlet 3.0).

Ho messo su un servlet con il doGet seguente metodo:

protected void doGet(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException { 
    HttpSession session = request.getSession(true); 
    String sid = session.getId(); 
    String sslId = (String) request.getAttribute(
       "javax.servlet.request.ssl_session"); 
    String uri = request.getRequestURI(); 
    OutputStream out = response.getOutputStream(); 
    PrintWriter pw = new PrintWriter(out); 
    HashMap<String, Object> secrets; 
    Object secret = null; 
    Object notSecret; 
    Date d = new Date(); 

    notSecret = session.getAttribute("unprotected"); 
    if (notSecret == null) { 
     notSecret = "unprotected: " + d.getTime(); 
     session.setAttribute("unprotected", notSecret); 
    } 
    secrets = (HashMap<String, Object>) session.getAttribute("protected"); 
    if (secrets == null) { 
     secrets = new HashMap<String, Object>(); 
     session.setAttribute("protected", secrets); 
    } 
    if (sslId != null) { 
     if (secrets.containsKey(sslId)) 
      secret = secrets.get(sslId); 
     else { 
      secret = "protected: " + d.getTime(); 
      secrets.put(sslId, secret); 
     } 
    } 
    response.setContentType("text/plain"); 
    pw.println(MessageFormat.format("URI: {0}", new Object[] { uri })); 
    pw.println(MessageFormat.format("SID: {0}", new Object[] { sid })); 
    pw.println(MessageFormat.format("SSLID: {0}", new Object[] { sslId })); 
    pw.println(MessageFormat.format("Info: {0}", new Object[] { notSecret })); 
    pw.println(MessageFormat.format("Secret: {0}", new Object[] { secret })); 
    pw.println(MessageFormat.format("Date: {0}", new Object[] { d })); 
    pw.close(); 
} 

Ho poi invocato un URL non protetto adatto utilizzando Firefox e Live estensione HTTP Basette, per ottenere il cookie di sessione. Questa è stata la risposta inviata quando ho navigato a

http://localhost:8080/EchoWeb/unprotected 

(il mio web.xml, come la vostra, protegge solo/user/* e/personali/*):

 
URI: /EchoWeb/unprotected 
SID: 9ACCD06B69CA365EFD8C10816ADD8D71 
SSLID: null 
Info: unprotected: 1254034761932 
Secret: null 
Date: 27/09/09 07:59 

successiva, ho cercato di accedere a un URL protetto

http://localhost:8080/EchoWeb/personal/protected 

e, come previsto, mi sono reindirizzato a

https://localhost:8443/EchoWeb/personal/protected 

e la risposta è stata

 
URI: /EchoWeb/personal/protected 
SID: 9ACCD06B69CA365EFD8C10816ADD8D71 
SSLID: 4abf0d67549489648e7a3cd9292b671ddb9dd844b9dba682ab3f381b462d1ad1 
Info: unprotected: 1254034761932 
Secret: protected: 1254034791333 
Date: 27/09/09 07:59 

Si noti che l'ID del cookie/sessione è la stessa, ma ora abbiamo una nuova SSLID. Ora proviamo a falsificare il server usando il cookie di sessione.

ho creato uno script Python, spoof.py:

import urllib2 

url = "https://localhost:8443/EchoWeb/personal/protected" 
headers = { 
    'Host': 'localhost:8080', 
    'User-Agent': 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3', 
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 
    'Accept-Language': 'en-gb,en;q=0.5', 
    'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', 
    'Cookie' : 'JSESSIONID=9ACCD06B69CA365EFD8C10816ADD8D71' 
} 
req = urllib2.Request(url, None, headers) 
response = urllib2.urlopen(req) 
print response.read() 

Ora, non c'è bisogno di conoscere Python, in particolare - Sto solo cercando di inviare una richiesta HTTP a un (diverso) risorsa protetta con lo stesso ID di sessione nel Cookie. Ecco la risposta, quando mi sono imbattuto il mio script spoof due volte:

 
C:\temp>spoof 
URI: /EchoWeb/personal/protected 
SID: 9ACCD06B69CA365EFD8C10816ADD8D71 
SSLID: 4abf0eafb4ffa30b6579cf189c402a8411294201e2df94b33a48ae7484f22854 
Info: unprotected: 1254034761932 
Secret: protected: 1254035119303 
Date: 27/09/09 08:05 


C:\temp>spoof 
URI: /EchoWeb/personal/protected 
SID: 9ACCD06B69CA365EFD8C10816ADD8D71 
SSLID: 4abf0eb184cb380ce69cce28beb01665724c016903650539d095c671d98f1de3 
Info: unprotected: 1254034761932 
Secret: protected: 1254035122004 
Date: 27/09/09 08:05 

Avviso nelle risposte di cui sopra che i dati della sessione (un valore con un timestamp di 1254034761932) che è stato fissato nel primo, la richiesta non protetto, è stato inviato in tutta , perché Tomcat sta utilizzando la stessa sessione perché l'ID di sessione è lo stesso. Questo è ovviamente non sicuro. Tuttavia, ricorda che gli ID SSL erano diversi ogni volta e se si utilizza quelli per digitare i dati della sessione (ad esempio, come mostrato), si dovrebbe essere sicuri. Se rinfresco la mia scheda di Firefox, ecco la risposta:

 
URI: /EchoWeb/personal/protected 
SID: 9ACCD06B69CA365EFD8C10816ADD8D71 
SSLID: 4abf0d67549489648e7a3cd9292b671ddb9dd844b9dba682ab3f381b462d1ad1 
Info: unprotected: 1254034761932 
Secret: protected: 1254034791333 
Date: 27/09/09 08:05 

Si noti che lo SSLID è lo stesso come per la richiesta di Firefox in precedenza. Pertanto, il server può distinguere le sessioni utilizzando il valore dell'ID SSL. Si noti in particolare che i "dati protetti" sono gli stessi per ogni richiesta effettuata dalla sessione di Firefox, ma diversi per per ciascuna sessione di spoofing e anche diversi dalla sessione di Firefox.

+0

Grandi spiegazioni! Ma ho ancora una domanda: come funziona, che la tua ultima chiamata con firefox manda il vecchio SSLID. Come si costruisce questo ID? Questa funzionalità è documentata ovunque? – Marcel

2

Penso che funzioni in questo modo. Non puoi basare il tuo controllo di accesso sulla sessione. Devi usare altri parametri. È necessario aggiungere l'autenticazione e utilizzare il controllo basato sui ruoli.

In Tomcat esiste una protezione ma esattamente opposta. Se si ottiene una sessione in area protetta, quella sessione non viene trasferita all'area non protetta. Tomcat ottiene ciò impostando un flag "sicuro" sul cookie in modo che il cookie non venga inviato alle connessioni HTTP.

+0

Non eseguo il controllo accessi in base alla sessione. Il precedente web.xml è solo una piccola parte dell'intero web.xml. Naturalmente ho implementato un metodo di autenticazione basato su JAAS. Cerco di essere un po 'più chiaro nella mia descrizione. – Marcel

2

Suggerisco di modificare l'ID sessione quando si autentica la sessione.
In questo modo il vecchio sessionId diventa inutile e il dirottamento di sessione è impossibile.
Per cambiare la sessionId in un servlet container:

  • copiare tutti gli attributi della sessione corrente su una collezione temporanea
  • session.invalidate()
  • session = req.getSession (true)
  • riempire la nuova sessione con gli attributi della collezione temp

Informazioni su SSLID, si noti che sia client sia server sono liberi di chiudere la connessione in qualsiasi tempo. Una volta chiuso, si verificherà un nuovo handshake SSL e verrà generato un nuovo SSID. Quindi, IMO SSLID non è un modo affidabile per tenere traccia (o aiutare a tracciare) le sessioni.

Problemi correlati