禁用JavaWeb应用中URL上包含的jsessionid

本文介绍了一种通过自定义过滤器来禁用JavaWeb应用中URL携带jsessionid的方法,该方法能有效提升应用的安全性和美观性。
Java Web 应用似乎总有这样的情况,有事没事总是要在 URL 后面加上个 jsessionid,而且似乎不能使用配置的方式直接禁用 URL 传递 sessionid,这样,就比较容易造成安全性的问题,或者在浏览器地址栏里留下一堆很不好看的地址,在 Struts2 中,使用了 url 标签的所有链接,甚至 CSS, JS 这样的东西,都会加上 jsessionid,如何去禁用呢,搜索国内的相关文章,无功而返,询问我们过去的架构师,也没有做过,只好想办法去找国外的网站,找到了这样的一篇文章。

http://randomcoder.com/articles/jsessionid-considered-harmful

通过加入 Filter 的方式过滤掉 URL 中包含的 jsessionid,再重新包装 Response 返回给浏览器。

因为没有太多东西,就不多解释了,大家拿了用就可以了。

import  javax.servlet. * ;
import  javax.servlet.http.HttpServletRequest;
import  javax.servlet.http.HttpServletResponse;
import  javax.servlet.http.HttpServletResponseWrapper;
import  javax.servlet.http.HttpSession;
import  java.io.IOException;

/**
 * Servlet filter which disables URL-encoded session identifiers.
 * <p/>
 * <pre>
 * Copyright (c) 2006, Craig Condit. All rights reserved.
 * <p/>
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * <p/>
 *   * Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 * <p/>
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * </pre>
 
*/
@SuppressWarnings(
" deprecation " )
public   class  DisableUrlSessionFilter  implements  Filter {

    
/**
     * Filters requests to disable URL-based session identifiers.
     
*/
    
public   void  doFilter(ServletRequest request, ServletResponse response, FilterChain chain)  throws  IOException, ServletException {
        
//  skip non-http requests
         if  ( ! (request  instanceof  HttpServletRequest)) {
            chain.doFilter(request, response);
            
return ;
        }

        HttpServletRequest httpRequest 
=  (HttpServletRequest) request;
        HttpServletResponse httpResponse 
=  (HttpServletResponse) response;

        
//  clear session if session id in URL
         if  (httpRequest.isRequestedSessionIdFromURL()) {
            HttpSession session 
=  httpRequest.getSession();
            
if  (session  !=   null ) session.invalidate();
        }

        
//  wrap response to remove URL encoding
        HttpServletResponseWrapper wrappedResponse  =   new  HttpServletResponseWrapper(httpResponse) {
            @Override
            
public  String encodeRedirectUrl(String url) {
                
return  url;
            }

            @Override
            
public  String encodeRedirectURL(String url) {
                
return  url;
            }

            @Override
            
public  String encodeUrl(String url) {
                
return  url;
            }

            @Override
            
public  String encodeURL(String url) {
                
return  url;
            }
        };

        
//  process next request in chain
        chain.doFilter(request, wrappedResponse);
    }

    
/**
     * Unused.
     
*/
    
public   void  init(FilterConfig config)  throws  ServletException {
    }

    
/**
     * Unused.
     
*/
    
public   void  destroy() {
    }
<think>我们有一个明确的问题:在JavaWeb应用中,当使用iframe嵌套页面且用户禁用了Cookie时,Java框架(如Tomcat)会自动将jsessionid附加到URL中(即URL重写),这可能导致会话ID暴露。我们需要防止这种情况,避免会话ID出现在URL中。分析:当浏览器禁用Cookie时,服务器无法通过Cookie来传递jsessionid,于是会使用URL重写的方式在URL后面追加jsessionid(如:;jsessionid=xxxx)。这在iframe中可能会导致安全问题,因为URL中的会话ID可能会被外部页面通过referer等方式获取。目标:禁用Cookie时,阻止服务器将jsessionid附加到URL中。然而,完全禁用URL重写可能会使会话无法工作,因为服务器需要某种方式来跟踪会话。所以我们需要权衡:要么彻底不使用会话(即无状态),要么确保即使禁用Cookie也不会进行URL重写(但这样会话就会丢失)。由于问题明确要求防止jsessionid暴露到URL中,我们可以考虑以下方案:1.彻底禁用会话:这样就不会有jsessionid,自然不会有暴露问题。2.在iframe中确保会话跟踪机制只通过Cookie传递,禁用URL重写,但如果用户禁用Cookie,那么会话将无法使用。所以我们需要确保在iframe中不要使用会话?或者,如果必须使用会话,我们可以要求用户启用Cookie。但是,用户的问题是在禁用Cookie的情况下如何避免暴露?所以我们必须处理禁用Cookie的情况。参考站内引用:[^1]说明了当第一次请求时,服务器会创建session并设置jsessionid的Cookie(如果支持Cookie的话),同时如果客户端不支持Cookie,则必须使用URL重写(在链接、重定向时加上jsessionid)。[^2]展示了如何通过EL表达式获取Cookie中的jsessionid。[^3]提到了通过Cookie共享Session的一种跨域方案(但需要设置根域名的Cookie)。因此,我们有以下思路:思路1:禁止使用会话(即无状态),这样就不会有jsessionid问题。但若应用需要会话,此方法不可行。思路2:禁止URL重写。Tomcat等容器支持配置URL重写行为。我们可以配置容器不进行URL重写,这样即使客户端禁用了Cookie,服务器也不会在URL中添加jsessionid,但是这样会导致会话无法在禁用Cookie的客户端中继续(因为无法传递jsessionid)。这可能导致会话丢失,但避免了暴露。思路3:在iframe嵌套的页面中,强制使用Cookie来传递jsessionid(不采用URL重写),并确保即使客户端禁用了Cookie,也不进行URL重写。但这样,客户端禁用Cookie的情况下会话会失效。我们可以通过技术手段检测客户端是否支持Cookie,如果不支持则给出提示或使用其他方式(比如Token)来维持状态。具体实施方法:方法一:在应用禁用会话(推荐彻底无状态)-在web.xml中配置session-timeout为0(即立即过期)或使用无状态架构(如RESTful应用,使用Token代替Session)。方法二:关闭URL重写(在Tomcat等服务器配置)-在Tomcat的context.xml中设置`<ContextdisableURLRewriting="true">`。这样,Tomcat将不会进行URL重写。注意:这意味着不支持Cookie的客户端将无法使用会话。方法三:确保我们的应用在生成URL时不进行URL重写。-在编写链接和重定向时,不要使用response.encodeURL()或response.encodeRedirectURL()方法。因为这两个方法就是用来在URL上添加jsessionid的(当检测到客户端不支持Cookie时)。但是,如果使用框架,可能无法避免框架内部使用这些方法。方法四:使用token代替session(如JWT)-将会话状态存储在客户端token中(需加密签名),每次请求通过HTTPHeader(如Authorization)传递,这样完全不依赖Cookie和URL重写,也就不会出现jsessionid暴露的问题。方法五:为iframe页面设置专用的Session机制-在iframe页面中,我们单独处理会话状态。例如,父页面通过postMessage传递token给iframe页面,iframe页面将token存储在内存中,每次请求通过自定义HTTPHeader传递。根据问题背景,用户主要关心iframe嵌套时的安全问题,且要求禁用Cookie(说明浏览器禁用了Cookie),所以采用方法二(禁用URL重写)或方法四(使用token)可能更合适。步骤示例(以方法二为例,在Tomcat中禁用URL重写):1.打开Tomcat的conf/context.xml文件(或者如果是项目特定配置,则编辑项目META-INF/context.xml)。2.在Context元素中添加属性:disableURLRewriting="true"3.重启Tomcat。这样,Tomcat就不会在URL中添加jsessionid了。但注意:这意味着禁用URL重写的功能,因此如果客户端不支持Cookie,会话将不能维持。但是,这避免了jsessionid出现在URL中。另外,我们也可以考虑结合方法五:使用自定义的token传递方式,完全避免使用Session。具体可以这样:-在父页面生成一个安全的token(例如一个随机的UUID,并存储在服务器的缓存中,同时设置有效期),然后将这个token通过URL参数或者postMessage传递给iframe的页面。-iframe页面获取到这个token,后续的请求都带上这个token(放在HTTPHeader中)。-服务器端根据这个token来恢复会话状态(从缓存中获取)。这样,既不依赖Cookie,也不依赖URL重写,而且token在URL中时也可以通过HTTPS和短生命周期来降低风险,或者完全不通过URL传递(通过postMessage)。考虑到安全性,我们应该避免将会话ID暴露在URL中,所以推荐使用无状态token(JWT)或者通过HTTPHeader传递token的方式。下面给出一个简化的方案(使用无状态JWT):步骤:1.用户登录后,服务器生成一个JWT(包含用户信息,并签名),在父页面中,将这个JWT通过某种方式(比如放在父页面的全局变量中)传递给iframe。例如:父页面通过URL参数、postMessage或直接写入iframe的src(如果同源)将JWT传递给iframe页面。2.iframe页面获取JWT后,在后续的请求中都带上这个JWT(例如放在Authorization头中)。3.服务器验证JWT的签名,然后从中提取用户信息。这样,完全不使用Session,也就没有jsessionid。实现示例:父页面(假设和iframe页面同源):```html<iframesrc="https://iframe.example.com/page?jwt=xxx"></iframe>```但是将token放在URL中仍然有暴露的风险(比如浏览器历史记录、referer等)。所以更安全的方式是通过postMessage传递。父页面:```html<iframeid="myframe"src="https://iframe.example.com/page"></iframe><script>//假设已经获得了jwtvarjwt="xxx";document.getElementById('myframe').onload=function(){this.contentWindow.postMessage(jwt,'https://iframe.example.com');};</script>```iframe页面:```javascriptwindow.addEventListener('message',function(event){if(event.origin==='https://parent.example.com'){//保存jwtlocalStorage.setItem('jwt',event.data);//或者直接保存到内存变量中(更安全,关闭页面即消失)}});```然后,在iframe页面的后续请求中,使用JavaScript设置请求头(如使用jQuery):```javascript$.ajaxSetup({beforeSend:function(xhr){xhr.setRequestHeader('Authorization','Bearer'+jwt);}});```注意:如果父页面和iframe页面跨域,需要正确处理跨域问题(设置CORS,以及验证postMessage的origin)。总结:在iframe中避免暴露jsessionid的最佳实践是使用无状态token(如JWT)代替Session,并通过HTTPHeader传递。因此,回答结构如下:1.问题分析:当禁用Cookie时,服务器使用URL重写将jsessionid附加到URL中,在iframe中可能导致安全问题。2.解决方案:有两种主要思路。a)在服务器配置中禁用URL重写(方法二)并接受不支持Cookie的客户端无法使用会话的事实。-配置Tomcat:disableURLRewriting="true"b)使用无状态token(如JWT)代替Session(推荐)。-详细步骤:父页面传递token给iframe页面(通过postMessage),iframe页面将token放在HTTPHeader中发送请求。我们推荐使用方案b,因为无状态token更加安全且可控,而且解决了跨域iframe的会话问题。注意:如果应用已经重度依赖Session,迁移到无状态token可能需要重构。如果用户无法迁移到无状态token,则可以选择在容器层面禁用URL重写(方法二)并确保客户端启用Cookie(如果客户端禁用了Cookie,则让会话失效)。生成相关问题:1.如何安全地在不同域名的iframe之间传递敏感数据?2.如何使用JWT实现无状态会话?3.在JavaWeb应用中如何配置Tomcat禁用URL重写?4.如何通过SpringSecurity实现JWT认证?5.如何在父页面和iframe之间进行跨域通信?</think> ### 禁用Cookie时防止JSessionID暴露在URL中的解决方案 在Java Web中,当用户禁用Cookie时,服务器会通过URL重写方式自动附加`jsessionid`参数(如`;jsessionid=xxx`)。在iframe嵌套场景中,这可能导致会话ID暴露在浏览器地址栏或Referer头中,带来安全风险。以下是综合解决方案: --- #### 方案一:彻底禁用URL重写(推荐) 在Web容器配置中直接禁用URL重写功能,阻止自动追加`jsessionid`: ```xml <!-- Tomcat的conf/context.xml --> <Context disableURLRewriting="true"> </Context> ``` **效果**: 1. 禁用Cookie时不再生成URL重写 [^1] 2. 完全消除jsessionid暴露风险 3. **副作用**:禁用Cookie后会话跟踪失效,需替代方案 --- #### 方案二:强制Cookie传输(兼容方案) 通过代码覆盖`HttpServletResponse`方法,阻止URL重写: ```java public class SafeResponseWrapper extends HttpServletResponseWrapper { @Override public String encodeURL(String url) { // 始终返回原始URL return url; } @Override public String encodeRedirectURL(String url) { return url; } } // 在过滤器中应用 public class SessionFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) { HttpServletResponse response = (HttpServletResponse) res; chain.doFilter(req, new SafeResponseWrapper(response)); } } ``` **原理**: 拦截URL编码方法,禁止写入jsessionid [^1] --- #### 方案三:无状态会话替代(安全方案) 完全避免使用Session机制,改用无状态认证: 1. **JWT认证**: ```java // 生成无状态Token String jwt = Jwts.builder() .setSubject(username) .setExpiration(new Date(System.currentTimeMillis()+3600000)) .signWith(SignatureAlgorithm.HS512, SECRET_KEY) .compact(); // 返回给前端存储 response.addHeader("Authorization", "Bearer " + jwt); ``` 2. **Token验证拦截器**: ```java public class JwtFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) { String token = ((HttpServletRequest)req).getHeader("Authorization"); Claims claims = Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token.replace("Bearer ", "")) .getBody(); // 验证通过后才继续请求 } } ``` **优势**: - 完全不依赖Cookie或URL参数 - 天然防御会话劫持 - 适合iframe嵌套场景 [^3] --- #### 补充安全措施 1. **设置SameSite属性**(防止CSRF): ```java // 明确设置Cookie属性 Cookie sessionCookie = new Cookie("JSESSIONID", sessionId); sessionCookie.setHttpOnly(true); sessionCookie.setSecure(true); // 仅HTTPS sessionCookie.setSameSite("Strict"); // 禁用跨域携带 ``` 2. **Referrer Policy防护**: ```html <!-- iframe页面头部添加 --> <meta name="referrer" content="no-referrer"> ``` --- ### 选择建议 | 方案 | 适用场景 | 安全性 | 实现复杂度 | |------|----------|--------|------------| | 禁用URL重写 | 内部系统/低安全需求 | ★★☆☆☆ | 低 | | 强制Cookie传输 | 兼容旧系统过渡期 | ★★★☆☆ | 中 | | **无状态会话** | 对安全性要求高的场景 | ★★★★★ | 高 | **推荐路径**: **先禁用URL重写(方案一) → 逐步迁移到无状态会话(方案三)**,同时在过渡期设置SameSite和Referrer Policy增强防护。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值