[ACCEPTED]-How to prevent tomcat session hijacking?-tomcat

Accepted answer
Score: 12

If it's a recent version of Tomcat, you 40 may not have a problem. However, this depends 39 on your checking the SSL ID associated with 38 the session. This is available using code 37 such as

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

(Note that the attribute key may 36 change in the future to javax.servlet.request.ssl_session_id - as part of the 35 Servlet 3.0 spec).

I set up a servlet with 34 the following doGet method:

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();
}

I then invoked a suitable 33 unprotected URL using Firefox and the Live 32 HTTP Headers extension, to get the session 31 cookie. This was the response sent when 30 I navigated to

http://localhost:8080/EchoWeb/unprotected

(my web.xml, like yours, only 29 protects /user/* and /personal/*):

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

Next, I 28 tried to access a protected URL

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

and, as expected, I 27 got redirected to

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

and the response was

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

Notice 26 that the cookie/session ID is the same, but 25 we now have a new SSLID. Now, let's try 24 to spoof the server using the session cookie.

I 23 set up a Python script, 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()

Now, you don't 22 need to know Python, particularly - I'm 21 just trying to send an HTTP request to a 20 (different) protected resource with the 19 same session ID in the Cookie. Here's the 18 response when I ran my spoof script twice:

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

Notice 17 in the above responses that the session 16 data (a value with a timestamp of 1254034761932) which 15 was set in the first, unprotected request, has 14 been sent throughout, because Tomcat is 13 using the same session because the session 12 ID is the same. This is of course not secure. However, note 11 that the SSL IDs were different each time and if you use those to key 10 into your session data (e.g. as shown), you 9 should be safe. If I refresh my Firefox 8 tab, here's the response:

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

Notice that the 7 SSLID is the same as for the earlier Firefox request. So, the 6 server can tell the sessions apart using 5 the SSL ID value. Notice particularly that 4 the "protected data" is the same for each 3 request made from the Firefox session, but 2 different for each of the spoofed sessions and also 1 different from the Firefox session.

Score: 2

I think it works like this by design. You 9 can't base your access control on session. You 8 need to use other parameters. You need to 7 add authentication and use role-based control.

In 6 Tomcat, there is protection but exactly 5 opposite. If you get a session in secure 4 area, that session is not transfered to 3 unprotected area. Tomcat achieves this by 2 setting "secure" flag on the cookie so the 1 cookie is not sent to the HTTP connections.

Score: 2

I suggest to change the sessionId when you 9 authenticate the session.
In this way the 8 old sessionId becomes useless and session 7 hijacking is impossible.
To change the sessionId 6 in a servlet container:

  • copy all the attributes of the current session on a temp collection
  • session.invalidate()
  • session = req.getSession(true)
  • fill the new session with the attributes from the temp collection

About SSLID, please 5 note that both client and server are free 4 to close the connection at any time. When 3 closed a new SSL handshake will happen and 2 a new SSID generated. So, IMO SSLID is not 1 a reliable way to track (or help to track) sessions.

More Related questions