我有一个在Tomcat7服务器上运行的web应用程序。会话id为的cookie默认具有标志HttpOnly
和Secure
。我想禁用JSESSIONID
cookie的此标志。但它不会起作用。我已经在web.xml
文件中对此进行了更改,但它不起作用。
<session-config>
<session-timeout>20160</session-timeout>
<cookie-config>
<http-only>false</http-only>
<secure>false</secure>
</cookie-config>
</session-config>
我知道这是一个安全风险,因为如果攻击者发现xss漏洞,他就可以窃取cookie并劫持会话。
JSESSIONID
cookie应使用HTTP和HTTPS以及AJAX请求发送。
编辑:
我已通过在conf/context.xml
文件中添加以下选项成功禁用了HttpOnly
标志:
<Context useHttpOnly="false">
....
</Context>
如果你阅读tomcat的代码,你会发现:
// Always set secure if the request is secure
if (scc.isSecure() || secure) {
cookie.setSecure(true);
}
因此,如果请求是安全的(即来自https url或SSL端口),那么尝试在侦听器中使用sessionCookieConfig.setSecure(false);
或web.xml中的<cookie-config><secure>false</secure></cookie-config>
停用JSESSIONID cookie上的安全标志将不起作用,因为Tomcat会强制将安全标志设为true。
一种解决方案是在会话创建后立即使用请求过滤器修改服务器响应上的JSESSIONID cookie。这是我的实现(非常基本):
public class DisableSecureCookieFilter implements javax.servlet.Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if(request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
request = new ForceUnsecureSessionCookieRequestWrapper((HttpServletRequest) request, (HttpServletResponse) response);
}
chain.doFilter(request, response);
}
@Override
public void destroy() { }
public static class ForceUnsecureSessionCookieRequestWrapper extends HttpServletRequestWrapper {
HttpServletResponse response;
public ForceUnsecureSessionCookieRequestWrapper(HttpServletRequest request, HttpServletResponse response) {
super(request);
this.response = response;
}
@Override
public HttpSession getSession(boolean create) {
if(create) {
HttpSession session = super.getSession(create);
updateCookie(response.getHeaders("Set-Cookie"));
return session;
}
return super.getSession(create);
}
@Override
public HttpSession getSession() {
HttpSession session = super.getSession();
if(session != null) {
updateCookie(response.getHeaders("Set-Cookie"));
}
return session;
}
protected void updateCookie(Collection<String> cookiesAfterCreateSession) {
if(cookiesAfterCreateSession != null && !response.isCommitted()) {
// search if a cookie JSESSIONID Secure exists
Optional<String> cookieJSessionId = cookiesAfterCreateSession.stream()
.filter(cookie -> cookie.startsWith("JSESSIONID") && cookie.contains("Secure"))
.findAny();
if(cookieJSessionId.isPresent()) {
// remove all Set-Cookie and add the unsecure version of the JSessionId Cookie
response.setHeader("Set-Cookie", cookieJSessionId.get().replace("Secure", ""));
// re-add all other Cookies
cookiesAfterCreateSession.stream()
.filter(cookie -> !cookie.startsWith("JSESSIONID"))
.forEach(cookie -> response.addHeader("Set-Cookie", cookie));
}
}
}
}
}
和在web.xml中:
<filter>
<filter-name>disableSecureCookieFilter</filter-name>
<filter-class>com.xxxx.security.DisableSecureCookieFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>disableSecureCookieFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
请记住,启用不安全的cookie会绕过一个重要的https安全性(为了从http到https的平稳过渡,我不得不这样做)
我在Tomcat中没有找到解决方案,但如果您使用apache作为反向代理,则可以执行以下操作:
Header edit* Set-Cookie "(JSESSIONID=.*)(; Secure)" "$1"
CCD_ 10,其将在返回的途中对报头进行屏蔽以移除安全标志。虽然不漂亮,但如果这是关键的话,它是有效的。
除了上面George Powell针对Apache的解决方案外,如果您在IIS上,您可以按如下方式解决它:
- 安装IIS URL重写模块
- 将以下内容添加到web.config
<rewrite>
<outboundRules>
<rule name="RemoveSecureJessionID">
<match serverVariable="RESPONSE_Set-Cookie" pattern="^(.*JSESSIONID.*)Secure;(.*)$" />
<action type="Rewrite" value="{R:1}{R:2}" />
</rule>
</outboundRules>
</rewrite>
这个解决方案来自Pete Freitag的博客
如上所述,自从最近的Chrome更新(2017年1月)以来,这已经成为一个问题。