I need to set httpOnly and secure flags on session cookie in Google App Engine.
I tried the following in web.xml
:
I had the very same problem with Google App Engine, using Java 7 and Servlet 2.5, to add HttpOnly
and Secure
attributes to session cookies. I followed @bat_venti 's answer - which helped very much, thank you! - but had some trouble to make it work, so I'm posting my own answer :)
I created a SecurityFilter
class to apply the session cookies attributes to .jsp
requests, just like the following:
import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;
public class SecurityFilter implements javax.servlet.Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// wrap the response
response = new SecureCookieSetter((HttpServletResponse)response);
// touch the session
((HttpServletRequest) request).getSession();
// overwriting the cookie with Secure and HttpOnly attribute set
((HttpServletResponse)response).setHeader("Set-Cookie", "JSESSIONID=" + ((HttpServletRequest)request).getSession().getId() + ";Path=/");
chain.doFilter(request, response);
}
public class SecureCookieSetter extends HttpServletResponseWrapper {
public SecureCookieSetter(HttpServletResponse response) {
super(response);
}
@Override
public void addCookie(Cookie cookie) {
cookie.setSecure(true);
super.addCookie(cookie);
}
@Override
public void addHeader(String name, String value) {
if ((name.equals("Set-Cookie")) && (!value.matches("(^|.*;)\\s*Secure"))) {
value = value + ";Secure;HttpOnly";
}
super.addHeader(name, value);
}
@Override
public void setHeader(String name, String value) {
if ((name.equals("Set-Cookie")) && (!value.matches("(^|.*;)\\s*Secure"))) {
value = value + ";Secure;HttpOnly";
}
super.setHeader(name, value);
}
}
}
(I created the SecureCookieSetter
class internally, because I would only use it in this filter, but feel free to put it on its own file).
After that, I edited the web.xml
file to use the filter whenever a .jsp
file is requested:
<web-app>
...
<filter>
<filter-name>SecurityFilter</filter-name>
<filter-class>path.to.my.filter.SecurityFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SecurityFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
...
</web-app>
(Obviously, replacing path.to.my.filter
for the real location of your class file).
I had the same problem with Google App Engine, but I wanted to add Secure
attribute to all cookies. The following shows how I've added Secure
attribute to all cookies. I'm almost sure that this solution will work for you just by substituting Secure
with HttpOnly
.
I've implemented a security filter and made a mapping to the pages that I want the Secure
attribute be set.
<filter>
<filter-name>Security Filter</filter-name>
<filter-class>common.SecurityFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Security Filter</filter-name>
<url-pattern>*.jsf</url-pattern>
</filter-mapping>
My first try was to wrap the response into my custom HttpServletResponseWrapper
. All was fine except the session cookie doesn't get the attribute. I debugged around and found that the session cookie is not added using the mechanism I've expected. I've then noticed that after you touch the session the session cookie is magically added to the response headers e.g. the headers now consists the line Set-Cookie: JSESSIONID=abcdef;Path=/
but the cookie wasn't added using the wrapper object that I've created. I've figured out that after I've touched the session I can set the cookie that I want with the attributes that I want. So the workaround was easy.
public class SecurityFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// wrap the response
response = new SecureCookieSetter((HttpServletResponse)response);
// touch the session
(HttpServletRequest)request.getSession();
// overwriting the cookie with Secure attribute set
((HttpServletResponse)response).setHeader("Set-Cookie", "JSESSIONID=" + ((HttpServletRequest)request).getSession().getId() + ";Path=/");
}
}
public class SecureCookieSetter extends HttpServletResponseWrapper {
public SecureCookieSetter(HttpServletResponse response) {
super(response);
}
@Override
public void addCookie(Cookie cookie) {
cookie.setSecure(true);
super.addCookie(cookie);
}
@Override
public void addHeader(String name, String value) {
if ((name.equals("Set-Cookie")) && (!value.matches("(^|.*;)\\s*Secure"))) {
value = value + ";Secure";
}
super.addHeader(name, value);
}
@Override
public void setHeader(String name, String value) {
if ((name.equals("Set-Cookie")) && (!value.matches("(^|.*;)\\s*Secure"))) {
value = value + ";Secure";
}
super.setHeader(name, value);
}
}
In my case the SecureCookieSetter class is not getting used. I also have my java web app running into the GAE. Below is code which is working fine in my case. Also its always recommeded to have other security related headers like max-age and others as below.
package com.securityFilter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import com.filters.XSSRequestWrapper;
public class SecurityFilter implements Filter {
protected static final Logger log = Logger.getLogger(SecurityFilter.class);
private static final String PRAGMA_KEY = "Pragma";
private static final String PRAGMA_VALUE = "no-cache";
private static final String STRICT_TRANSPORT_KEY = "strict-transport-security";
private static final String STRICT_TRANSPORT_VALUE = "max-age=604800";
private static final String SET_COOKIE = "Set-Cookie";
private static final String JSESSION_ID = "JSESSIONID=";
private static final String HTTP_ONLY = ";Secure;HttpOnly";
private static final String CACHE_CONTROL_KEY = "Cache-Control";
private static final String CACHE_CONTROL_VALUE = "no-store";
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
makeCookieSecured(response, httpServletRequest);
chain.doFilter(request, response);
}
private void makeCookieSecured(ServletResponse response,
HttpServletRequest httpServletRequest) {
Cookie[] cookies = httpServletRequest.getCookies();
HttpServletResponse httpResp = ((HttpServletResponse) response);
if (cookies != null) {
for(Cookie cookie :cookies){
if("JSESSIONID".equals(cookie.getName())) {
cookie.setValue(httpServletRequest.getSession().getId() + HTTP_ONLY);
cookie.setSecure(true);
cookie.setPath("/");
cookie.setMaxAge(604800);
}
}
}
httpResp.setHeader(SET_COOKIE, JSESSION_ID + httpServletRequest.getSession().getId() + HTTP_ONLY);
httpResp.setHeader(CACHE_CONTROL_KEY, CACHE_CONTROL_VALUE);
httpResp.setHeader(PRAGMA_KEY, PRAGMA_VALUE);
httpResp.setHeader(STRICT_TRANSPORT_KEY, STRICT_TRANSPORT_VALUE);
}
private void createJSONErrorResponse(ServletResponse response)
throws IOException {
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write("Please provide valid input, You might have provided some special characters which is not allowed");
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}