как обновить JSESSIONID cookie после входа в систему - PullRequest
26 голосов
/ 17 ноября 2011

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

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

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

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

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

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

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

Ответы [ 10 ]

38 голосов
/ 17 ноября 2011

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

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"%>
5 голосов
/ 07 января 2014

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

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

Теперь предположим, что мы запустили session.invalidate до того, как пострадавший пользователь вошел в систему. Допустим, файл cookie изначально имел значение abc. При запуске session.invalidate значение abc удаляется из сеанса сервера.

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

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

  • Пользователь делает GET /login.html
  • Вернуть страницу входа с тем файлом cookie, который в данный момент находится в браузере
  • Пользователь вводит учетные данные и нажимает кнопку отправки
  • Приложение проверяет учетные данные
  • При обнаружении, что учетные данные были правильными. session.invalidate () запускается .. уничтожает старый cookie.
  • СЕЙЧАС создайте новый файл cookie, используя request.getSession (true)

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

Вот хороший блог об этой проблеме - https://blog.whitehatsec.com/tag/session-fixation/

4 голосов
/ 24 ноября 2014

Я следовал следующим образом, чтобы восстановить новый сеанс из старого сеанса.Надеюсь, вам это поможет.

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));
        }
    }
2 голосов
/ 20 февраля 2017

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

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

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

  1. создать новую сессию
  2. атрибуты переноса старого сеанса.
2 голосов
/ 18 ноября 2011

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

  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, который можно добавить в context.xml.Он будет обрабатывать выдачу нового JSESSIONID после аутентификации.Более подробная информация доступна здесь: https://issues.apache.org/bugzilla/show_bug.cgi?id=45255. Запись для клапана будет выглядеть следующим образом: <Valve className="org.apache.catalina.authenticator.FormAuthenticator" changeSessionIdOnAuthentication="true"/>

1 голос
/ 12 февраля 2018

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

1 голос
/ 17 ноября 2011

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

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

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

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

Или все ваши страницы по https

0 голосов
/ 19 мая 2017

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

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

}
0 голосов
/ 29 марта 2017

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

0 голосов
/ 31 декабря 2016
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);
}  
...