如何在登录后刷新JSESSIONID cookie

问题描述:

我工作的产品受到潜在客户的严格安全审核,他们对于Tomcat在认证发生之前设置JSESSIONID Cookie感到不安。也就是说,Tomcat在我们的无状态登录页面加载时,但在登录之前设置此cookie。如何在登录后刷新JSESSIONID cookie

他们建议以下任一操作:

  1. 问题的新JSESSIONID的cookie登录后
  2. 防止在登录页面首先被设置JSESSIONID的cookie(即验证已经发生之前)

我一直在钻研与本网站相关的所有JSESSIONID,并且找不到简单的答案。我只是希望有一些想法。对于每一个我最好的解决方案是:

  1. 登录之后,克隆复制的所有属性,旧的会话无效,创建一个新的,复制的值,它与请求关联的会话(减去ID) ,并希望这有效。
  2. 在最初加载登录页面之前,在链的最末端创建一个servlet过滤掉JSESSIONID cookie。然后希望登录请求在没有JSESSIONID集的情况下运行。

我必须睡一会儿,但早上会尝试这些。从人那里获得一些比我更聪明的反馈或更好的建议将会非常棒 - 像你一样!

无论如何,我会在这里发布我的结果,因为它好像很多其他人一直想要做类似的事情。

你不会刷新后,但只是之前。当执行登录操作首先做到:

HttpSession session = request.getSession(false); 
if (session!=null && !session.isNew()) { 
    session.invalidate(); 
} 

然后做:

HttpSession session = request.getSession(true); // create the session 
// do the login (store the user in the session, or whatever) 

FYI你用这一招解决是http://www.owasp.org/index.php/Session_Fixation

最后你可以禁用自动会话创建,只有创建当你真的需要它时。如果你使用JSP,你可以这样做:

<%@page contentType="text/html" 
     pageEncoding="UTF-8" 
     session="false"%> 
+0

有趣的东西。我正在使用Wicket 1.3,而且我似乎无法找到设置'session = false'的方法(视图方不是基于JSP的)。我现在要尝试getSession(false)想法...谢谢! –

+0

在登录之前使会话失效导致Wicket混乱(只是重定向到登录页面)。仍然搞这个...... –

+1

@RichardsonHeights:看看https://issues.apache.org/jira/browse/WICKET-1767。它似乎记录和解决。 – cherouvim

问题是JSESSIONID在浏览器中可见,或者它根本在cookie中设置?我假设你的情况是后者。

1.issue一个新的登录

这之后JSESSIONID cookie是默认的Tomcat行为如果从HTTP在登录时切换为HTTPS。旧的被丢弃,并产生一个新的。

如果您的登录本身是通过HTTP,我想这是审计人员的另一个安全问题;)

或者是所有的页面通过https?

+0

问题是,JSESSIONID cookie在浏览器中设置,并在Firefox cookie查看器中可见(例如)。正如Cherouvim指出的那样,这是“会话固定”安全漏洞。 有趣的HTTP/HTTPS切换...该应用程序只能运行在https上,不过。 –

我发现的两件事可能对别人有帮助。

  1. 如果您使用的是Apache Wicket,那么在版本1.4之后有一个解决方案。我的应用程序仍然在1.3,所以我没有意识到,但我可以很容易地在我自己的WebSession类中将它移回端口。 Wicket 1.4为WebSession添加了一个replaceSession()方法,该方法效果很好。你可以在认证后立即调用它,你会得到一个新的JSESSIONID。它基本上为我解决了这个问题。更多的信息在这里:https://issues.apache.org/jira/browse/WICKET-1767

  2. 在5.5.29版本之后有一个Apache Tomcat阀可以添加到context.xml中。它将在验证后处理发布新的JSESSIONID。更多信息请点击这里:https://issues.apache.org/bugzilla/show_bug.cgi?id=45255。阀门的入口应该是这样的:<Valve className="org.apache.catalina.authenticator.FormAuthenticator" changeSessionIdOnAuthentication="true"/>

我不能@ cherouvim的回答上面的评论,因为我没有足够的积分。新会话ID应该在用户成功登录后设置,以避免会话固定。我会试着解释我的推理。

会话固定实际上意味着攻击者以某种方式诱骗用户使用攻击者已知的值。为了简单起见,让我们假设攻击者走到用户的桌面,使用Firebug并编辑用户的cookie。现在,当用户登录时,他/她将用攻击者控制的cookie登录。由于攻击者也知道这个值,他/她将刷新浏览器,映射到该会话ID(受害者的资源)的资源将被提供给他们。这是会议固定。正确?

现在我们假设我们在受害者用户登录之前运行了session.invalidate。让我们假设cookie最初有一个abc值。运行session.invalidate abc值将从服务器的会话中清除。

现在出现我不同意的部分。您建议在用户实际登录之前生成一个新会话(输入用户名和密码并单击提交)。这无疑会导致一个新的cookie生成,但它会在用户的浏览器登录之前。因此,如果攻击者可以再次编辑“prelogin”cookie,攻击仍然存在,因为即使用户登录后也会使用相同的cookie。

我认为这是正确的流程。

  • 用户做一个GET /login.html
  • 返回登录页面与任何cookie是目前存在于浏览器
  • 用户输入凭据,并点击提交
  • 应用验证凭据
  • 在发现凭据是正确的。 session.invalidate()运行..销毁旧的cookie。
  • 现在可以生成使用request.getSession(真)

这意味着什么,是,即使攻击者设法诱骗您登录之前,使用一个控制值,你就是新的cookie仍然保护..如果应用程序强制更改您登录后的值。

这里是关于这个问题一个很好的博客 - https://blog.whitehatsec.com/tag/session-fixation/

我按照下面的方式来重新从旧会话的新的会话。希望你能从中受益。

private void regenerateSession(HttpServletRequest request) { 

    HttpSession oldSession = request.getSession(); 

    Enumeration attrNames = oldSession.getAttributeNames(); 
    Properties props = new Properties(); 

    if (attrNames != null) { 
     while (attrNames.hasMoreElements()) { 
      String key = (String) attrNames.nextElement(); 
      props.put(key, oldSession.getAttribute(key)); 
     } 

     //Invalidating previous session 
     oldSession.invalidate(); 
     //Generate new session 
     HttpSession newSession = request.getSession(true); 
     attrNames = props.keys(); 

     while (attrNames.hasMoreElements()) { 
      String key = (String) attrNames.nextElement(); 
      newSession.setAttribute(key, props.get(key)); 
     } 
    } 

session=request.getSession(true); 
Enumeration keys = session.getAttributeNames();  
HashMap<String,Object> hm=new HashMap<String,Object>(); 
while (keys.hasMoreElements()) 
{ 
    String key = (String)keys.nextElement(); 
    hm.put(key,session.getValue(key)); 
    session.removeAttribute(key);  
} 
session.invalidate(); 
session=request.getSession(true); 
for(Map.Entry m:hm.entrySet()) 
{ 
    session.setAttribute((String)m.getKey(),m.getValue()); 
    hm.remove(m); 
} 

使用的弹簧,你应该使用SessionFixationProtectionStrategy

<property name="sessionAuthenticationStrategy" ref="sas"/> 
... 
<bean id="sas" class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy"/> 

在视察source code,你会看到,这类似于harsha89的方法:将

  1. 创建一个新的会话旧的会话的
  2. 转院属性。

如果您使用的是Tomcat,并希望在全球范围内应用该到所有的servlet它们使用Tomcat的身份验证机制,你可以写一个阀门,迫使这种行为,如本sample code.

如果您正在使用像jboss 4那样的老版本的jboss,然后在session.invalidate()调用之后简单地调用request.getSession(true)将不会更改会话标识。

如果您不想使用阀门并想在操作类中更改会话标识,则可以使用反射来归档相同的标识,因为CatalinaRequest将不会直接在您的操作类中提供。

示例代码

private HttpSession changeSessionId(HttpServletRequest request) 
{ 
    HttpSession oldSession = request.getSession(false); 
    HttpSession newSession = null; 

    try 
    { 
     //get all cookies from request 
     Cookie[] cookies = request.getCookies(); 

     //Get all attribute from old session 
     Enumeration<Object> attrNames = oldSession.getAttributeNames(); 

     Properties attributFromOldSession = new Properties(); 

     while (attrNames.hasMoreElements()) 
     { 
      String key = (String)attrNames.nextElement(); 
      attributFromOldSession.put(key, oldSession.getAttribute(key)); 
     } 

     //Actual logic to change session id 

     Field catalinaRequestField; 

     //Getting actual catalina request using reflection 
     catalinaRequestField = request.getClass().getDeclaredField("request"); 
     catalinaRequestField.setAccessible(true); // grant access to (protected) field 
     Request realRequest = (Request)catalinaRequestField.get(request); 

     //Invalidating actual request 
     realRequest.getSession(true).invalidate(); 
     realRequest.setRequestedSessionId(null); 
     realRequest.clearCookies(); 

     //setting new session Id 
     realRequest.setRequestedSessionId(realRequest.getSessionInternal(true).getId()); 

     //Put back the cookies 
     for (Cookie cookie : cookies) 
     { 

      if (!"JSESSIONID".equals(cookie.getName())) 
      { 
       realRequest.addCookie(cookie); 
      } 
     } 

     // put attribute from old session 
     attrNames = attributFromOldSession.keys(); 

     while (attrNames.hasMoreElements()) 
     { 
      String key = (String)attrNames.nextElement(); 
      newSession.setAttribute(key, attributFromOldSession.get(key)); 
     } 
    } 
    catch (Exception e) 
    { 
     e.printStackTrace(); 
    } 
    return newSession; 

} 

HttpServletRequest.changeSessionId()可以使用在任何时间点来改变会话ID。