Java and HttpOnly

HttpOnly is an HTTP cookie property originally developed by Microsoft that makes cookies "non-scriptable" - any attempts to access the cookie value through JavaScript will fail.

HttpOnly mitigates the threat of session hijacking through cross-site scripting, but only partially - more on this later.

Until recently, HttpOnly was only supported in Internet Explorer 6, SP1 and up. Now, however, the latest version of FireFox supports HttpOnly.

It's easy to specify a cookie as HttpOnly, it raises the bar for an attacker, and it doesn't affect most regular functionality. So why not set your session identifier cookies to HttpOnly?

Well, if you're developing ASP.NET, PHP, or Ruby on Rails web applications, you're in luck. Just set a property, or change a config file, and you're golden.

But what about J2EE? Well, there is no HttpOnly property supported in the Cookie interface. You can set your own cookies to be HttpOnly:

response.setHeader("Set-Cookie", "originalcookiename=originalvalue; HTTPOnly");

But that doesn't work for JSESSIOND, the J2EE session identifier, since it is handled by the container. So, you're out of luck. Or are you?

<Pause for commercial break>

</Pause for commercial break>

Welcome back. After fiddling around with HacmeBooks, the Foundstone Free Tool for demonstrating common web application vulnerabilities in Java, I was able to get the HttpOnly property set on the JSESSIONID cookie.

Here's the code below (from a ServletFilter):

// Check if this is where the JSESSIONID is being set (assuming that JSESSIONID is the only cookie used)
if (response.containsHeader("SET-COOKIE"))
{
    String sessionid = request.getSession().getId();
    response.setHeader("SET-COOKIE", "JSESSIONID=" + sessionid + "; Path=/HacmeBooks; HttpOnly");
}
// Continue down the Filter Chain
chain.doFilter(request, response);

This code is far from ideal - it essentially replaces the JSESSIONID cookie set by the server, so any properties (path, expires, secure, etc.) that the server sets have to be specified in the code. It also won't work if other cookies besides JSESSIONID are being used (you could fix this by looking at the request and making sure JSESSIONID isn't already set).

However, until Java gets around to supporting HttpOnly cleanly, this is the best way I could figure to set this property on the JSESSIONID.

*BONUS*

I mentioned that there are some people who have taken HttpOnly to task for being an incomplete mitigation for XSS.

Jeremiah Grossman talks about using the HTTP TRACE verb to get access to HttpOnly cookies in the reflected request.

pdp suggests that HttpOnly is meaningless because most attackers don't really care about session hijacking through XSS - there are more damaging attacks that can be leveraged through XSS.

RSnake points out that in FireFox XMLHttpRequest can be used to access the cookie and bypass HttpOnly. Additionally, some older, obscure browsers (IE5 on Mac, WebTV) choke and die on the header instead of safely ignoring it.

Amit Klein suggest several different methods, including some mentioned above, for bypassing the HttpOnly protection.

I think for all of its shortcomings, HttpOnly is still a good idea. The bugs will get worked out, and everyone will be a little safer.

Published Monday, November 05, 2007 10:10 PM by Alex

Comments

# re: Java and HttpOnly@ Tuesday, November 06, 2007 2:38 AM

Does this really work? If the call to request.getSession() hasn't happened yet, then the response won't have the set-cookie header yet. Seems to me that the call to chain.doFilter() should come before the set-cookie check so the response will be filled in.

by owasp

# re: Java and HttpOnly@ Wednesday, November 28, 2007 2:13 PM

Yes, indeed the code shown does work. After the chain.doFilter(), the response has already been sent to the client, so the response modification needs to occur before.

From "The Essentials of Filters"

java.sun.com/.../Filters.html

<quote>

The most important method in the Filter interface is the doFilter method, which is the heart of the filter. This method usually performs some of the following actions:

  * Examines the request headers

  * Customizes the request object if it wishes to modify request headers or data or block the request entirely

  * Customizes the response object if it wishes to modify response headers or data

  * Invokes the next entity in the filter chain. If the current filter is the last filter in the chain that ends with the target servlet, the next entity is the resource at the end of the chain; otherwise, it is the next filter that was configured in the WAR. It invokes the next entity by calling the doFilter method on the chain object (passing in the request and response it was called with, or the wrapped versions it may have created). Alternatively, it can choose to block the request by not making the call to invoke the next entity. In the latter case, the filter is responsible for filling out the response.

  * Examines response headers after it has invoked the next filter in the chain

  * Throws an exception to indicate an error in processing

</quote>

As far as calling request.getSession(), I believe that this must have already been called in a previous filter.

by Alex