как обновить файл cookie JSESSIONID после входа в систему

продукт, над которым я работаю, получил жесткий аудит безопасности потенциальным клиентом, и они расстроены тем, что Tomcat устанавливает файл cookie JSESSIONID до аутентификации. То есть Tomcat устанавливает этот файл cookie при загрузке страницы входа без состояния, но перед входом в систему.

Они предлагают следующее:

  1. выдайте новый файл cookie JSESSIONID после входа в систему
  2. запретить установку файла cookie JSESSIONID в первую очередь на странице входа в систему (т. е. до аутентификация произошла)

Я просматривал все, что связано с JSESSIONID на этом сайте, и не могу найти простого ответа. Я просто надеюсь на некоторые идеи. Мои лучшие решения для каждого из них:

  1. сразу после входа в систему клонируйте сеанс (минус id), скопировав все атрибуты, аннулировав старый сеанс, создав новый, скопировав значения, связав его с запросом и надеясь, что это сработает.
  2. создать фильтр сервлетов на самый конец цепочки, которая удаляет файл cookie JSESSIONID перед загрузкой страницы входа в систему. И тогда надеюсь, что запрос на вход работает без набора JSESSIONID.

Мне нужно немного поспать, но утром я попытаюсь это сделать. Было бы здорово получить обратную связь или лучшие предложения от людей, намного умнее меня-как вы!

независимо от того, я опубликую свои результаты здесь, потому что кажется, что многие другие люди были желая сделать что-то подобное.

10 ответов


вы не будете обновляться после, но только перед. При выполнении действия входа в систему сначала выполните:

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

Я не могу прокомментировать ответ @cherouvim выше, поскольку у меня недостаточно очков. Новый идентификатор сеанса должен быть установлен "после" успешного входа пользователя в систему, чтобы избежать фиксации сеанса. Я попытаюсь объяснить свои доводы.

фиксация сеанса эффективно означает, что злоумышленник каким-то образом обманул пользователя, используя значение, известное злоумышленнику. Для простоты предположим, что злоумышленник подошел к столу пользователя, использовал Firebug и отредактировал файл cookie пользователя. Теперь, когда пользователь входит в систему, он / она войдет в систему с контролируемым злоумышленником cookie. Поскольку злоумышленник также знает это значение, он обновит свой браузер, и ресурсы, сопоставленные с этим идентификатором сеанса (ресурсы жертвы), будут обслуживаться им. Это фиксация на сеансе. Правильно?

теперь предположим, что мы провели сессии.аннулировать прежде чем пользователь жертвы вошел в систему. Допустим, файл cookie изначально имел значение abc. На бегу сессии.аннулировать значение abc очищен от сеанса сервера.

теперь наступает та часть, где я не согласен. Вы предлагаете создать новый сеанс до того, как пользователь фактически войдет в систему (вводит имя пользователя и пароль и нажимает submit). Это, несомненно, приведет к созданию нового файла cookie, но он будет в браузере пользователя до входа в систему. Таким образом, если злоумышленник может отредактировать файл cookie "prelogin" снова, атака все еще сохраняется, так как тот же файл cookie будет использоваться даже после входа пользователя.

Я думаю, это правильный поток.

  • пользователь делает GET / login.HTML-код
  • вернуть страницу входа с любым cookie в настоящее время есть в браузере
  • пользователь вводит учетные данные и нажимает submit
  • приложение проверяет учетные данные
  • при обнаружении, что учетные данные были правильными. сессия.invalidate() будет работать ..уничтожить старое печенье.
  • Теперь создайте новый файл cookie, используя запрос.getSession (true)

Это означает, что даже если злоумышленнику удастся обмануть вас в использовании контролируемого значения до входа в систему, вы все еще защищены..поскольку приложение принудительно изменяет значение после входа в систему.

вот хороший блог об этом вопросе -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));
        }
    }

проблема в том, что JSESSIONID отображается в браузере или что он вообще устанавливается в cookie? Я предполагаю, что это последнее в вашем случае.

1.выдайте новый файл cookie JSESSIONID после входа

Это поведение Tomcat по умолчанию если вы переключаетесь с http на https во время входа в систему. Старое отбрасывается, и создается новое.

Если Ваш логин сам по http, я думаю, что это еще одна безопасность выпуск для аудиторов;)

или все ваши страницы через https?


две вещи, которые я нашел, которые могут помочь другим.

  1. Если вы используете Apache Wicket, есть решение для этого после версии 1.4. Мое приложение все еще на 1.3, поэтому я не понял, но я смог очень легко вернуть его в свой собственный класс WebSession. Wicket 1.4 добавляет метод replaceSession () в WebSession, который отлично работает. Вы можете вызвать его сразу после аутентификации, и вы получите новый JSESSIONID. Это в основном решило эту проблему для меня. Больше информация здесь: https://issues.apache.org/jira/browse/WICKET-1767.

  2. после версии 5.5.29 доступен клапан Apache Tomcat, который вы можете добавить в контекст.XML. Он будет обрабатывать выдачу нового JSESSIONID после аутентификации. Более подробная информация доступна здесь:https://issues.apache.org/bugzilla/show_bug.cgi?id=45255. Запись для клапана будет выглядеть так:<Valve className="org.apache.catalina.authenticator.FormAuthenticator" changeSessionIdOnAuthentication="true"/>


HttpServletRequest.changeSessionId() можно использовать для изменения идентификатора сессии в любой момент времени.


при использовании spring вы должны использовать SessionFixationProtectionStrategy.

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

при осмотре исходный код, вы увидите, что это похоже на подход harsha89: это будет

  1. создать новый сеанс
  2. атрибуты передачи старого сеанса.

Если вы используете Tomcat и хотите применить это глобально ко всем сервлетам, которые используют механизм аутентификации Tomcat, вы можете написать клапан, чтобы заставить это поведение, как показано в этом пример кода.


Если вы используете более старую версию jboss, например jboss 4, просто вызовите запрос.getSession (true) после сеанса.вызов invalidate () не изменит идентификатор сеанса.

Если вы не хотите использовать valve и хотите изменить идентификатор сеанса в классе действий, его можно архивировать с помощью отражения, потому что 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;

}

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