如何在登录后刷新JSESSIONID cookie

时间:2011-11-17 05:41:51

标签: tomcat login session-cookies jsessionid

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

他们建议以下两种方式之一:

  1. 登录后发出新的JSESSIONID cookie
  2. 防止在登录页面上的第一个位置设置JSESSIONID cookie(即,在进行身份验证之前)
  3. 我一直在浏览这个网站上与JSESSIONID相关的所有内容,但却找不到简单的答案。我只是希望有一些想法。我最好的解决方案是:

    1. 在登录后立即通过复制所有属性,使旧会话无效,创建新会话,复制值,将其与请求相关联以及希望它起作用来克隆会话(减去id)。
    2. 在链的最末端创建一个servlet过滤器,在最初加载登录页面之前剥离出JSESSIONID cookie。然后希望登录请求能够在没有设置JSESSIONID的情况下运行。
    3. 我得睡一觉,但早上会尝试这些。能够比我更聪明的人得到一些反馈或更好的建议真是太棒了 - 就像你一样!

      无论如何,我会在这里发布我的结果,因为似乎很多其他人都想做类似的事情。

10 个答案:

答案 0 :(得分:37)

你不会在之前但之前刷新。首先执行登录操作时执行:

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)

仅供参考,您使用此技巧解决的问题是http://www.owasp.org/index.php/Session_Fixation

最后,您可以禁用自动会话创建,并仅在您真正需要时创建会话。如果您使用JSP,则可以通过以下方式执行:

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

答案 1 :(得分:4)

由于我没有足够的分数,我无法评论@ 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(true)
  • 生成新的Cookie

这意味着,即使攻击者在登录之前设法诱使您使用受控值,您仍然受到保护......应用程序在您登录后强制更改该值。

以下是关于此问题的好博客 - https://blog.whitehatsec.com/tag/session-fixation/

答案 2 :(得分:3)

我已按照以下方式从旧会话重新生成新会话。希望你能从中受益。

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));
        }
    }

答案 3 :(得分:1)

问题是JSESSIONID在浏览器中是可见的还是在cookie中设置了?我假设你的情况就是后者。

  

1.登录后发出新的JSESSIONID cookie

如果您在登录时从http切换到https ,则这是默认的Tomcat行为。旧的被丢弃并生成一个新的。

如果您的登录本身超过http,我认为这是审核员的另一个安全问题;)

或者您的所有网页都通过https?

答案 4 :(得分:1)

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

  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"/>

答案 5 :(得分:1)

使用spring时,您应该使用SessionFixationProtectionStrategy

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

在检查source code时,你会发现这与harsha89的方法类似:它会

  1. 创建新会话
  2. 传递旧会话的属性。

答案 6 :(得分:1)

HttpServletRequest.changeSessionId()可用于在任何时间更改会话ID。

答案 7 :(得分:0)

如果您正在使用Tomcat并希望将此全局应用于使用Tomcat身份验证机制的所有servlet,则可以编写Valve来强制执行此行为,如此sample code.

所示

答案 8 :(得分:0)

如果您使用的是旧版本的jboss,例如jboss 4,那么在session.invalidate()调用之后调用request.getSession(true)将不会更改会话ID。

如果您不想使用Valve并希望更改动作类中的会话ID,则可以使用反射归档,因为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;

}

答案 9 :(得分:-1)

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);
}