Seam is quite a good and secure framework as far as frameworks go. However, with a simple ServletFilter, it can be made just a little bit more secure, and maybe help you pass that next audit. In this post you’ll see how to define a Seam servlet filter to secure your website just a little bit more.
The first things any good penetration testing is going to look for are your cookie characteristics. There are simple reasons for that: it’s easy, and many applications get them wrong. They are:
- Secure flag
- HttpOnly flag
- Post-login session key regeneration
The cookie’s Secure flag
The secure flag is telling the browser to only send this cookie over secure (SSL) channels. This is irrelevant if your application is not SSL-secured (but penetration testing is rarely done on these anyway).
There’s no way for Seam to automatically know whether your application is deployed on an SSL-protected server or not. Therefore you need to take care of it yourself.
Normally, you would configure this in the container’s configuration, but this isn’t always possible, as not all containers support this, especially if your container does not use Servlet 3.0 specification.
The Servlet Filter below adds the secure flag to your cookie, and does it smartly by checking first to see if you are using a secure connection. This is good for applications that may be deployed either way (e.g. when your development environment isn’t SSL-protected).
The cookie’s HttpOnly flag
Setting the HttpOnly flag on a cookie helps protect against some forms of attacks related to cookie stealing. This flag was introduced by (surprise surprise!) Microsoft with Internet Explorer 6, to prevent a vulnerability where an attacker could access your cookies directly, using Javascript, and send them over to their website.
This recommendation is a favourite with security consultants, and you’re bound to have that raised if you don’t set the flag on your cookies. I personally believe that it’s a low priority issue (see here for a short discussion) – but it’s quite easy to do, so why not.
Again, this setting should really be performed in the container, but if it doesn’t support Servlet 3.0 you may have to revert to using the Servlet Filter.
Post-login session key regeneration
This is quite a good one. See, when the user first lands on your website, your Seam application (via the container) sets a cookie on their computer, with a JSESSIONID parameter. This is the session id all JEE containers use to track user sessions in the cookie.
By default, after the user logs in successfully, this session id remains the same. Hence, if an attacker can get hold of this session id – they can then create a cookie on their computer that has the same session id, and use it as an authenticated user. This attack is called session fixation.
Obviously, this is not a very good vulnerability to have in your website, and the Servlet Filter below takes care of it by regenerating the session id after a successful login.
The Servlet Filter
In Seam, it is very easy to add a Servlet Filter to your application without touching any xml. In essence, the filter becomes a Seam component.
You can do everything using Seam’s annotations like so:
@Name( "cookieFilter" )
@Install( precedence = Install.FRAMEWORK )
@BypassInterceptors
@Filter( within = { "org.jboss.seam.web.rewriteFilter" } )
public class CookieFilter extends AbstractFilter {
.
.
}
The two important annotations here are the @BypassInterceptors – which tells Seam to not apply any of its interceptors to this component. This is important because the Servelt Filter is sort of a pseudo-component that acts like an interceptor itself.
The second important annotation is the @Filter – telling Seam where this filter needs to go in the filter chain. For this filter, the right place is within Seam’s rewriteFilter, because the main action here entails rewriting the HTTP response.
public static final String SECURE_FLAG = "; Secure";
public static final String JSESSION_ATTR = "JSESSIONID=";
public static final String PATH_ATTR = "; Path=";
public static final String HTTPONLY_FLAG = "; HttpOnly";
public static final String POST_LOGON_ATTR = "postLogon";
@SuppressWarnings("unchecked")
@Override
public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException {
.
.
The only method we need to override on the AbstractFilter is the doFilter() method. This method takes a ServletRequest and a ServletResponse objects, together with the FilterChain.
The request and response objects are the actual request the user’s browser submitted, and the response it is going to receive. The FilterChain is the chain of filters configured for the application – of which the CookieFilter is a member.
It is important to link back the chain at the end of the doFilter method, otherwise it will not be completed.
The first part of doFilter() below tests whether the response object contains the “SET-COOKIE” header. We do not want the application to send a new cookie with every request made of the server, so we only let the servlet filter piggy-back on responses that already have this flag set.
Now, because the cookie is sent as a header on the response object, we can’t just add the HttpOnly and secure to it – we need to recreate the cookie header completely.
The code excerpt below does just that by retrieving the session id (the JSESSIONID attribute), and the context path from the existing header.
It then tests to see whether the connection is secure (using the isSecure() method on the request object) – and sets the flag accordingly.
Finally, the code re-sets the “SET-COOKIE” header, using all the existing content, and adding the HttpOnly flag, and also the secure flag if required.
HttpServletResponse httpResponse = ( HttpServletResponse ) response;
HttpServletRequest httpRequest = (HttpServletRequest) request;
if ( httpResponse.containsHeader( SET_COOKIE ) ) {
String sessionid = httpRequest.getSession().getId();
String contextPath = httpRequest.getContextPath();
String secure = "";
if ( request.isSecure() ) {
secure = SECURE_FLAG;
}
httpResponse.setHeader( SET_COOKIE, JSESSION_ATTR + sessionid + PATH_ATTR + contextPath + HTTPONLY_FLAG + secure );
}
To generate a new session-id after a successful login attempt, we are going to use a little flag of our own. On our web application, the first request will have the ‘SET-COOKIE’ header, but subsequent requests will not.
We use this knowledge to find the first post-login request that goes to a page (and not, say, an image) – this is done by testing the query string, which in a Seam application must not be null (because it will have the cid= attribute on it).
We also test for the presence of POST_LOGON_ATTR – an attribute we are going to manually add to the session after we change the session id. This will ensure that we don’t create a new session id on each new request.
When the first post-logon request on a session hits this filter, the if statement below returns ‘true’. The filter then needs to invalidate the session and create a new one (thereby generating a new JSESSIONID).
However, the filter must make sure that the new session retains all the information from the old one. Therefore, before invalidating the session, it saves a map of all the session attributes, and copies them over to the new one.
if (httpRequest.getQueryString() != null && httpRequest.getSession().getAttribute( POST_LOGON_ATTR ) ==null ) {
//Save existing session attributes
HashMap<String, Object> old = new HashMap<String, Object>();
Enumeration<String> keys = (Enumeration<String>) httpRequest.getSession().getAttributeNames();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
old.put(key, httpRequest.getSession(false).getAttribute(key));
}
//Invalidate existing session and create a new one
httpRequest.getSession().invalidate();
HttpSession session = httpRequest.getSession(true);
//Copy attributes onto the new session
for (String key : old.keySet()) {
session.setAttribute(key, old.get(key));
}
//Set the flag to signal that the JSESSIONID has already been changed.
session.setAttribute( POST_LOGON_ATTR, "true" );
//Add the 'SET-COOKIE' header, to send the new JSESSIONID to the browser.
String secure = "";
if ( request.isSecure() ) {
secure =SECURE_FLAG;
}
httpResponse.setHeader( SET_COOKIE, JSESSION_ATTR + httpRequest.getSession().getId() + PATH_ATTR +
httpRequest.getContextPath() + HTTPONLY_FLAG + secure );
}
}
//Remember to re-link the filter chain.
chain.doFilter( request, response );
}
This is it! your filter is now fully configured as a Seam component, and will work on your request. You can use Firefox’s View Cookies extension to verify that your JSESSIONID changes post login, and that the HttpOnly and secure flags are added to the cookie.
Below is the full Servlet Filter class, including the import statements.
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.annotations.web.Filter;
import org.jboss.seam.web.AbstractFilter;
@Scope( ScopeType.APPLICATION )
@Name( "cookieFilter" )
@Install( precedence = Install.FRAMEWORK )
@BypassInterceptors
@Filter( within = { "org.jboss.seam.web.rewriteFilter" } )
public class CookieFilter extends AbstractFilter {
public static final String SET_COOKIE = "SET-COOKIE";
public static final String SECURE_FLAG = "; Secure";
public static final String JSESSION_ATTR = "JSESSIONID=";
public static final String PATH_ATTR = "; Path=";
public static final String HTTPONLY_FLAG = "; HttpOnly";
public static final String POST_LOGON_ATTR = "postLogon";
@SuppressWarnings("unchecked")
@Override
public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException {
HttpServletResponse httpResponse = ( HttpServletResponse ) response;
HttpServletRequest httpRequest = (HttpServletRequest) request;
if ( httpResponse.containsHeader( SET_COOKIE ) ) {
String sessionid = httpRequest.getSession().getId();
String contextPath = httpRequest.getContextPath();
String secure = "";
if ( request.isSecure() ) {
secure = SECURE_FLAG;
}
httpResponse.setHeader( SET_COOKIE, JSESSION_ATTR + sessionid + PATH_ATTR + contextPath + HTTPONLY_FLAG + secure );
} else {
if (httpRequest.getQueryString() != null && httpRequest.getSession().getAttribute( POST_LOGON_ATTR ) ==null ) {
HashMap<String, Object> old = new HashMap<String, Object>();
Enumeration<String> keys = (Enumeration<String>) httpRequest.getSession()
.getAttributeNames();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
old.put(key, httpRequest.getSession(false).getAttribute(key));
}
httpRequest.getSession().invalidate();
HttpSession session = httpRequest.getSession(true);
for (String key : old.keySet()) {
session.setAttribute(key, old.get(key));
}
session.setAttribute( POST_LOGON_ATTR, "true" );
String secure = "";
if ( request.isSecure() ) {
secure =SECURE_FLAG;
}
httpResponse.setHeader( SET_COOKIE, JSESSION_ATTR + httpRequest.getSession().getId() + PATH_ATTR +
httpRequest.getContextPath() + HTTPONLY_FLAG + secure );
}
}
chain.doFilter( request, response );
}
}
Popularity: 29% [?]
2 Responses to Securing your Seam application cookies
Leave a Reply Cancel reply
Recent Comments
- Medovarszki: This post saved my hair from falling out but yeah, I beat my head against the wall now too
- Shehryar: Probably the ebst spring web mvc tutorial I have read, explain everything in great detail. i loved it, this...
- armand: You do not resize the image. You just display it smaller using html tag attributes. It would be nice to...
- COTP: ** Just to clarify, in CakeErrorController.php (the copy in app/Controller/) public function beforeRender() {...
- COTP: For CakePHP 2.0+, you can copy lib/Cake/Controller/CakeErrorC ontroller.php to app/Controller/CakeErrorCon...
- Medovarszki: This post saved my hair from falling out but yeah, I beat my head against the wall now too
Categories
- Android (1)
- appengine (3)
- Be nice to your users (6)
- CakePHP (13)
- General (5)
- Googlemaps (3)
- Java (11)
- javaScript (4)
- jQuery (11)
- Seam (2)
- Security (4)
- Spring MVC (1)
- Spring Webflow (2)
- User Interface (5)
- Wordpress (2)
Archives
- January 2012 (2)
- December 2011 (1)
- July 2011 (2)
- June 2011 (4)
- May 2011 (1)
- December 2010 (3)
- November 2010 (1)
- August 2010 (2)
- July 2010 (2)
- June 2010 (4)
- May 2010 (4)
- April 2010 (5)
- March 2010 (3)

We’re a group of volunteers and starting a brand new initiative in a community. Your weblog supplied us valuable information to function on. You have done a marvellous work!
it’s very clear. It is useful for me a lot. Good..job