如何配置Payara禁止为根路径/颁发JSESSIONID 防范会话固定漏洞
Payara Server 重复JSESSIONID问题修复方案
根因说明
Payara Server 5.2021.10 基于GlassFish内核实现,默认会在用户首次访问动态资源时自动创建会话、下发路径为/的JSESSIONID,当应用部署在/my-app上下文路径、登录完成后容器又会下发路径匹配应用上下文的JSESSIONID,两个Cookie同时留存不仅会导致会话匹配混乱,还确实存在会话固定漏洞风险。可通过以下两类方案实现登录前不返回JSESSIONID:
方案1:web.xml 容器配置(无代码侵入,优先推荐)
直接在应用WEB-INF/web.xml中添加如下配置,从容器层面关闭提前创建会话的逻辑、统一Cookie规则:
<session-config> <!-- 仅当代码显式调用会话创建逻辑时才生成JSESSIONID,禁止请求自动触发会话创建 --> <session-properties> <property name="enableSessionCreationRequirement" value="true"/> </session-properties> <cookie-config> <!-- 统一所有会话Cookie的作用路径为应用上下文,彻底杜绝根路径/的冗余JSESSIONID --> <path>/my-app</path> <http-only>true</http-only> <secure>true</secure> <!-- 登录成功后自动更换Session ID,从容器层面阻断会话固定漏洞 --> <change-session-id-on-login>true</change-session-id-on-login> </cookie-config> <!-- 会话超时时间根据业务需求配置即可 --> <session-timeout>30</session-timeout> </session-config>
配置完成后重启应用,未触发显式会话创建的公开请求(比如登录页访问、登录参数校验阶段)都不会返回JSESSIONID。
方案2:Java代码侧控制(适配无法调整容器配置的场景)
如果无法修改web.xml全局配置,可通过代码逻辑从请求入口拦截控制:
- 所有公开访问的资源(登录页、静态资源、登录校验接口未通过前),禁止调用
request.getSession()、request.getSession(true)方法,这两个方法会强制触发新会话创建;如果需要判断用户是否已登录,仅调用request.getSession(false),该方法在无有效会话时返回null,不会生成新会话。 - 如果项目使用JSP作为视图层,需要在所有公开访问的JSP页面顶部添加
<%@ page session="false" %>指令,关闭JSP渲染时自动创建会话的默认逻辑;如果使用JSF,需要在对应视图配置中关闭预创建会话的开关。 - 新增全局过滤器,在请求完成后清理未登录场景下被容器自动添加的冗余JSESSIONID,核心实现如下:
@WebFilter(filterName = "sessionPreventFilter", urlPatterns = "/*") public class SessionPreventFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; chain.doFilter(req, resp); // 未登录判断逻辑根据自身系统鉴权规则调整,示例为容器内置身份校验的判断方式 if (request.getUserPrincipal() == null) { Collection<String> cookieHeaders = response.getHeaders("Set-Cookie"); // 清理所有路径为/的JSESSIONID下发逻辑 for (String cookieHeader : cookieHeaders) { if (cookieHeader.startsWith("JSESSIONID") && cookieHeader.contains("Path=/;")) { response.setHeader("Set-Cookie", cookieHeader.replaceAll("JSESSIONID=[^;]*;", "JSESSIONID=deleted;Max-Age=0;")); } } } } }
- 登录校验通过后,再显式调用
request.getSession()创建用户会话,同时调用Servlet 3.1+提供的request.changeSessionId()方法更换会话标识,彻底规避会话固定风险。
注意:如果应用引入了Spring Security、Shiro等安全框架,需要同步检查框架配置中是否存在提前创建会话的逻辑,将公开路径的会话创建策略调整为
never,避免框架层提前触发JSESSIONID下发。
内容的提问来源于stack exchange,提问作者jhansen
相关产品推荐
相关产品推荐

