web-dev-qa-db-ja.com

ログイン後にJSESSIONID Cookieを更新する方法

私が取り組んでいる製品は潜在的な顧客から厳しいセキュリティ監査を受けており、認証が行われる前にTomcatがJSESSIONID Cookieを設定することに動揺しています。つまり、Tomcatは、ステートレスログインページがロードされたとき、ログイン前にこのCookieを設定します。

次のいずれかを提案します。

  1. ログイン後に新しいJSESSIONID Cookieを発行します
  2. ログインページの最初の場所にJSESSIONID Cookieが設定されるのを防ぎます(つまり、認証が行われる前)

私はこのサイトでJSESSIONID関連のすべてを熟読しており、簡単な答えは見つかりません。私はいくつかのアイデアを期待しています。それぞれに最適なソリューションは次のとおりです。

  1. ログイン直後に、すべての属性をコピーし、古いセッションを無効にし、新しいセッションを作成し、値をコピーし、それをリクエストに関連付け、それが機能することを期待して、セッションを複製します。
  2. ログインページが最初にロードされる前にJSESSIONID Cookieを削除するサーブレットフィルターをチェーンの最後に作成します。そして、JSESSIONIDが設定されていなくてもログイン要求がうまくいくことを願っています。

眠りにつく必要がありますが、朝にこれらを試します。あなたのような私よりはるかに賢い人々からフィードバックやより良い提案を得るのは素晴らしいことです!

とにかく、他の多くの人々が同様のことをしたいと思っているように見えるので、ここに私の結果を投稿します。

26
Nathan Beach

更新後ではなく、直前に更新されます。ログインアクションを最初に実行する場合:

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

上記の@cherouvimの答えについては、十分なポイントがないためコメントできません。セッションの固定を避けるため、ユーザーが正常にログインした後、新しいセッションIDを設定する必要があります。私の推論を説明しようとします。

セッション固定とは、事実上、攻撃者が何らかの方法でユーザーをだまして攻撃者に知られている値を使用させることです。簡単にするために、攻撃者がユーザーの机に行き、Firebugを使用し、ユーザーのCookieを編集したと仮定しましょう。これで、ユーザーがログインすると、攻撃者が制御するCookieでログインします。攻撃者はこの値も知っているため、ブラウザを更新し、そのセッションIDにマップされたリソース(被害者のリソース)が提供されます。それがセッション固定です。正しい?

被害者のユーザーがログインする前にsession.invalidateを実行したとしましょう。最初にcookieの値がabcだったとします。 session.invalidateを実行すると、値abcはサーバーのセッションから削除されます。

今私が同意しない部分が来ます。ユーザーが実際にログインする前に新しいセッションを生成することをお勧めします(ユーザー名とパスワードを入力し、送信をクリックします)。これにより疑いなく新しいCookieが生成されますが、ログインする前にユーザーのブラウザに表示されます。したがって、攻撃者が「プレログイン」Cookieを再度編集できる場合、ユーザーがログインした後でも同じCookieが使用されるため、攻撃は継続します。

これが正しい流れだと思います。

  • ユーザーがGET /login.htmlを実行します
  • ブラウザに現在存在するCookieを含むログインページを返す
  • ユーザーが資格情報を入力し、送信をクリックします
  • アプリケーションは資格情報を検証します
  • 資格情報が正しいことを発見したとき。 session.invalidate()が実行され、古いCookieが破棄されます。
  • NOWrequest.getSession(true)を使用して新しいCookieを生成します

これは、攻撃者がログイン前に制御された値を使用するように仕向けたとしても、保護されていることを意味します。アプリケーションはログイン後に値を強制的に変更するためです。

この問題に関する良いブログがあります- https://blog.whitehatsec.com/tag/session-fixation/

5
LDE

次の方法に従って、古いセッションから新しいセッションを再生成しました。あなたがそれから恩恵を受けることを願っています。

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

私が見つけた2つのことは、他の人に役立つかもしれません。

  1. Apache Wicketを使用している場合は、バージョン1.4以降にこれに対する解決策があります。私のアプリはまだ1.3なので、気づきませんでしたが、自分のWebSessionクラスで非常に簡単に移植できました。 Wicket 1.4はreplaceSession()メソッドをWebSessionに追加しますが、これは非常に効果的です。認証後すぐに呼び出すことができ、新しいJSESSIONIDを取得します。基本的にこの問題は解決しました。詳細はこちら: https://issues.Apache.org/jira/browse/WICKET-1767

  2. Context.xmlに追加できるバージョン5.5.29以降のApache Tomcat Valveが利用可能です。認証後に新しいJSESSIONIDの発行を処理します。詳細はこちらから入手できます: https://issues.Apache.org/bugzilla/show_bug.cgi?id=45255 バルブのエントリは次のようになります:<Valve className="org.Apache.catalina.authenticator.FormAuthenticator" changeSessionIdOnAuthentication="true"/>

2
Nathan Beach

スプリングを使用する場合は、SessionFixationProtectionStrategyを使用する必要があります。

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

ソースコード を調べると、これはharsha89のアプローチに似ていることがわかります。

  1. 新しいセッションを作成する
  2. 古いセッションの属性を転送します。
2
Matthias M

JSESSIONIDがブラウザに表示されるのか、それともCookieに設定されるのかという問題はありますか?あなたの場合は後者だと思います。

1.ログイン後に新しいJSESSIONID Cookieを発行する

これは、Tomcatのデフォルトの動作ですhttpからhttpsに切り替えた場合ログイン時。古いものは破棄され、新しいものが生成されます。

ログイン自体がhttpを介している場合、それは監査人にとって別のセキュリティ問題だと思います;)

または、すべてのページがhttps経由ですか?

1
JoseK

HttpServletRequest.changeSessionId()を使用して、いつでもセッションIDを変更できます。

1
bhawnesh dipu

Tomcatを使用しており、Tomcatの認証メカニズムを使用するすべてのサーブレットにこれをグローバルに適用する場合、この サンプルコード に示すように、この動作を強制するバルブを記述できます。

0
David Leppik

Jboss 4のような古いバージョンのjbossを使用している場合、session.invalidate()呼び出しの後にrequest.getSession(true)を呼び出してもセッションIDは変更されません。

Valveを使用したくない場合、アクションクラスでセッションIDを変更したい場合、リアクションを使用して同じものをアーカイブできます。CatarinaRequestはアクションクラスで直接利用できないためです。

サンプルコード

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
Rahul Suman