Java Servlet Filters Part 2: Removing Cookies

Java Servlet Filters Part 2: Removing Cookies

Posted by Brad Wood
Jul 12, 2008 02:50:00 UTC
As promised, I have written a Java Servlet Filter to remove cookies from a request. If you don't know what a Servlet Filter is, read this blog. Let me start by saying while my filter does function like I wanted it to, it did NOT solve this problem. I'll blog on that next. UPDATE: Here are my findings Given that, I can't really think of a real-world use for this code, but I am always in favor of coding for the sake of learning.Ok, as I discussed in my previous post servlet filters are given access to the request before it is passed to the servlet (in our case CF) and it can modify the request as it sees fit. My goal is to be able to block certain named cookies that are sent from the client so they never reach the application. Cookies in their simplest form have a name and value. They are passed from the server to the client using a "set-cookie:" header and then the client passes them back to the server which each request using a "cookie:" header. In our scenario the client was passing back rogue cookies we didn't set which was causing needless errors in the logs. Back to Java now. The HttpServletRequest object lets you get cookies with the getCookies() method, but unfortunately it will not let you change the cookies in the object. This is where the HttpServletRequestWrapper class comes in handy-- it puts some smoke and mirrors in our bag of tricks. The wrapper's constructor takes a HttpServletRequest as an input and serves up its information. Any data modifications can be made by overriding the parent class's methods. Let's take a look at what I mean. Taking the Eclipse Java project I created in my previous post, I created a new class called requestFacade which extends the HttpServletRequestWrapper class. It has a constructor which simply defers to the super class, and a getCookies() method which overrides the super class. This means when anyone wants to call their getCookies() method they have to go through my overridden version first. This is what the class looks like:
[code]package com.filters;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import javax.servlet.ServletException;
import java.util.ArrayList;

public class requestFacade extends HttpServletRequestWrapper 
{
	
	private String[] removeArray = null;

	public requestFacade(HttpServletRequest request)
	{
		super(request);
	}
	
	public Cookie[] getCookies()
	{
		ArrayList newCookies;
		
		Cookie[] originalCookies = super.getCookies();

		if (originalCookies != null && removeArray !=null)
		{
			newCookies = new ArrayList();
			for(int i=0; i<originalCookies.length; i++)
			{
				boolean isOK = true;
				String thisCookieName = originalCookies[i].getName();
				//Filter out bad cookies
		        	for(int j=0; j<removeArray.length; j++)
	                	{
	                		if(thisCookieName.equalsIgnoreCase(removeArray[j]))
	                		{				                			
                				// Cookies with a forbidden name will be skipped
                				isOK = false;
	                		}
	                	}
		        	if(isOK)
	                	{
					newCookies.add(originalCookies[i]); //appends element to end of list	
	                	}
			}

			Cookie finalCookies[] = new Cookie[newCookies.size()];
			finalCookies=(Cookie[]) newCookies.toArray(finalCookies);
	

			return finalCookies;
		}
		return originalCookies;

	}
	
	public void setCookieRemoveList (String[] incomingArray)
	{
		this.removeArray = incomingArray;
	}
}
[/code]
There is also a setCookieRemoveList() method, but I'll get to that in a minute. Let's take a moment to go back to the testFilter class. Here is the latest version of it:
[code]package com.filters;

import javax.servlet.*;

import javax.servlet.http.*;
import java.io.IOException;
import javax.servlet.ServletException;
import com.filters.requestFacade;
import java.util.*;

public class testFilter implements Filter 
{
	private FilterConfig filterConfig = null;
	private String[] removeArray = null; 
	
	// This method is called once on server startup
	public void init(FilterConfig filterConfig) 
	{
	this.filterConfig = filterConfig;
	String removeParameter = filterConfig.getInitParameter("removeList");
	if(removeParameter != null)
	{
		System.out.println("Banned Cookies: " + removeParameter);
		StringTokenizer st = new StringTokenizer(removeParameter, ",");
		int tokenCount = st.countTokens();
		removeArray = new String[tokenCount];
		 
		for (int i = 0; i < tokenCount; i++) {
			removeArray[i] = st.nextToken();
		}
	
	}
	System.out.println("Brad Wood's " + filterConfig.getFilterName() + " filter loaded successfully.");
	
	}
	
	// This method is called for every request and needs to be thread safe.
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
	 throws java.io.IOException, javax.servlet.ServletException 
	{
		HttpServletRequest httpRequest = (HttpServletRequest)request;
		// This executes the next filter in line, or the target servlet.
			requestFacade modifiedRequest = new requestFacade(httpRequest);
			if(removeArray !=null)
			{
				modifiedRequest.setCookieRemoveList(removeArray);
			}
			chain.doFilter(modifiedRequest,response);
	}
	
	// This method is called once on server shut down
	public void destroy() 
	{
	this.filterConfig = null;
	}
}
[/code]
One of the additions to this class is in the constructor:
[code]String removeParameter = filterConfig.getInitParameter("removeList");
[/code]
You are able to pass in initialization parameters into your servlet filter by specifying them in your web.xml file like so:
[code]    <filter>
        <filter-name>testFilter</filter-name>
        <filter-class>com.filters.testFilter</filter-class>
        <init-param>
            <param-name>removeList</param-name>
            <param-value>Expires,path,domain,spam</param-value>
        </init-param>
    </filter> [/code]
When the servlet filter is loaded I parse the comma delimited list of cookie names to remove and store it in the class as an array of strings for later use. Also, on startup the following two lines are written to my log:
[code] Banned Cookies: Expires,path,domain,spam
Brad Wood's testFilter filter loaded successfully. [/code]
Now, down in my doFilter() method, you can see I create an instance of my filterFacade class like so:
[code] requestFacade modifiedRequest = new requestFacade(httpRequest);[/code]
Note, the doFilter() method is only given a servletRequest object, and it must be cast to an httpServletRequest object for the httpServletRequestWrapper constructor to accept it. Then I tell my new requestFacade object what cookie names it needs to block by calling the setCookieRemoveList() method:
[code]modifiedRequest.setCookieRemoveList(removeArray);[/code]
Now I pass in my modified façade to the rest of the chain and no one is the wiser:
[code] chain.doFilter(modifiedRequest,response);[/code]
When a filter later on the chain or the target servlet calls the getCookies() method in the request object, MY overridden method will get called. The first thing it does is get the original list of Cookies from the parent class:
[code] Cookie[] originalCookies = super.getCookies();[/code]
Then we create an empty ArrayList and populate it with the original cookies one at a time. Each cookie is inspected and if it matches of our banned list we omit it.
[code]if(thisCookieName.equalsIgnoreCase(removeArray[j]))
	{
		// Cookies with a forbidden name will be skipped
		isOK = false;
	}
[/code]
Once we are finished building the new ArrayList, we cast it into an array of Cookies, and return it as an imposter list of cookies. It's that simple. Ok, let's test! I exported everything to a jar and placed it in the ColdFusion class path and I updated web.xml to show the snippet above with the init-param. Restart ColdFusion. I created a simply test CF page which looped over the cookie scope and output their names and values.
[code]<cfoutput>
	<cfloop collection="#cookie#" item="i">
		#i#: #cookie[i]#
	</cfloop>
</cfoutput>
[/code]
I used the "Request Builder" feature of MS Fiddler to easily send a request to ColdFusion with any cookies I wanted. My request headers looked like this:
[code]GET / HTTP/1.1
User-Agent: Fiddler
Host: www.mytestserver.com
Cookie: spam=yum;tofu=yuck 
[/code]
You can see I am telling the server that I have two cookies, spam and tofu with values yum and yuck respectively. If you recall, "spam" is one of the cookie names I placed in the init-param to my servlet filter to be blocked. (The param values can obviously be changed, that's why I made them be passed in like that.) The full request I received back was:
[code]HTTP/1.1 200 OK
Date: Sat, 12 Jul 2008 05:40:24 GMT
Server: Apache/2.2.4 (Win32) JRun/4.0
Content-Type: text/html; charset=UTF-8
Content-Length: 6197

tofu: yuck [/code]
My tofu cookie made it to ColdFusion, but the spam cookie disappeared. Oh happy day! Well, there you have it. Let me know if you have any questions about the code, or have some Java pointers for me. My source code and compiled .jar file can be found here. I'm a little clunky on some of that stuff. I really miss handy functions like listtoarray()! NOTE: While the code in this post does what it's supposed to, it still didn't solve my original problem. Here is my followup blog looking into why this approach still doesn't get rid of our troublesome cookies.

 


Panos

Hello Brad. That was a really nice idea and i just tested it. We have a problem with the jsessionid appearing twice in our servers (domain and subdomain) which is actually a problem of IE. So the idea was to write a filter that will find the cookie (jsessionid) based on the getDomain function of the cookie and delete it.

BUT... i see no luck, the cookies, even if i remove them, they stay in my request

In the facade i do for(int j=0; j<finalCookies.length; j++) { System.out.println("Final: "+finalCookies[j].getName() + " : " + finalCookies[j].getDomain()); } return finalCookies;

The cookie i want to delete is not there, but still when i check with fiddler the cookie is there. I use CF 8 (the development version)

The code is exactly the same as yours.

So do you have any ideas???? I would really appreciate it Also any ideas on how to get the domain name of the cookie?

Thank you!

Panos

My bad man!!!! It works... im stupid ;) Thanks to my friend Pipiriki!

Site Updates

Entry Comments

Entries Search