Recently I've stumbled upon a feature in Tomcat 6.0.16:
- browser cookies containing symbols like : or = get broken in Tomcat when reading using request.getCookies() - only part of the cookie value is returned
- when writing such cookies using request.addCookie(), Tomcat automatically adds double quotes around the value
Arguable, such cookies are malformed and using them involves security risks.
In reality, such cookies are quite popular among web developers - they allow to put together some user preferences like that:
MyCookie=language=en:currency=EUR
Even Google uses cookies like that.
What options we have to solve the problem ?
- use the correct cookie format (with double quotes)
- stick to Tomcat 6.0.14 or older
- do some custom hack
Option 1 is the best if you can afford it (but you must still account for cookies already present in client browsers).
Option 2 is only a temporary solution - sooner or later you will need to upgrade (e.g. because of important security patches).
So the unfortunate souls like me are left with the Option 3. My cookie is used jointly by many web application and changing the format at once is not possible.
So here is the solution: write a servlet filter that does custom cookie reading and writing.
public class TomcatQuotedCookieFilter implements Filter { private static final String MY_COOKIE_NAME = "MyCookie"; public static class CookieWrappedRequest extends HttpServletRequestWrapper { public CookieWrappedRequest(HttpServletRequest request) { super(request); } @Override public Cookie[] getCookies() { // getCookies() returns correct numbers of cookies, but myCookie value might be broken Cookie[] cookies = super.getCookies(); int myCookieIndex = CookieUtils.findCookiePosition(MY_COOKIE_NAME, cookies); if (myCookieIndex == -1) { return cookies; } String cookieValue = CookieUtils.extractCookieValueFromHeader(MY_COOKIE_NAME, getHeader("Cookie")); if (cookieValue != null) { cookies[myCookieIndex] = new Cookie(MY_COOKIE_NAME, cookieValue); } return cookies; } } public static class CookieWrappedResponse extends HttpServletResponseWrapper { public CookieWrappedResponse(HttpServletResponse response) { super(response); } @Override public void addCookie(Cookie cookie) { // catch my cookie and apply custom formatting if (cookie.getName().equals(MY_COOKIE_NAME)) { addHeader("Set-Cookie", CookieUtils.formatSetCookieHeaderValue(cookie)); } else { super.addCookie(cookie); } } } public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { filterChain.doFilter(new CookieWrappedRequest((HttpServletRequest) request), new CookieWrappedResponse( (HttpServletResponse) response)); } // init, destroy... }
CookieUtils methods are left as an exercise for the reader :) Those might be dependent on the specific cookie format that you are using.
1 comment:
I intend to use parts of your code to fix a problem here at our institution (non profit higher education). Are you OK with that?
I think the vendor who provided us our portal would be interested in the solution I've developed from your code to fix a SSO issue. Can you send me an email at jtboehm( at ) kent (dot) edu so I can get them in touch with you? I have no idea if this would be something they would want to license, attribute, or whatnot...
Post a Comment