私が取り組んでいる製品は潜在的な顧客から厳しいセキュリティ監査を受けており、認証が行われる前にTomcatがJSESSIONID Cookieを設定することに動揺しています。つまり、Tomcatは、ステートレスログインページがロードされたとき、ログイン前にこの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)
参考までに、このトリックで解決しているのは http://www.owasp.org/index.php/Session_Fixation です
最後に、自動セッション作成を無効にして、本当に必要なときにのみセッションを作成できます。 JSPを使用する場合は、次のようにします。
<%@page contentType="text/html"
pageEncoding="UTF-8"
session="false"%>
上記の@cherouvimの答えについては、十分なポイントがないためコメントできません。セッションの固定を避けるため、ユーザーが正常にログインした後、新しいセッションIDを設定する必要があります。私の推論を説明しようとします。
セッション固定とは、事実上、攻撃者が何らかの方法でユーザーをだまして攻撃者に知られている値を使用させることです。簡単にするために、攻撃者がユーザーの机に行き、Firebugを使用し、ユーザーのCookieを編集したと仮定しましょう。これで、ユーザーがログインすると、攻撃者が制御するCookieでログインします。攻撃者はこの値も知っているため、ブラウザを更新し、そのセッションIDにマップされたリソース(被害者のリソース)が提供されます。それがセッション固定です。正しい?
被害者のユーザーがログインする前にsession.invalidateを実行したとしましょう。最初にcookieの値がabcだったとします。 session.invalidateを実行すると、値abcはサーバーのセッションから削除されます。
今私が同意しない部分が来ます。ユーザーが実際にログインする前に新しいセッションを生成することをお勧めします(ユーザー名とパスワードを入力し、送信をクリックします)。これにより疑いなく新しいCookieが生成されますが、ログインする前にユーザーのブラウザに表示されます。したがって、攻撃者が「プレログイン」Cookieを再度編集できる場合、ユーザーがログインした後でも同じ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));
}
}
私が見つけた2つのことは、他の人に役立つかもしれません。
Apache Wicketを使用している場合は、バージョン1.4以降にこれに対する解決策があります。私のアプリはまだ1.3なので、気づきませんでしたが、自分のWebSessionクラスで非常に簡単に移植できました。 Wicket 1.4はreplaceSession()メソッドをWebSessionに追加しますが、これは非常に効果的です。認証後すぐに呼び出すことができ、新しいJSESSIONIDを取得します。基本的にこの問題は解決しました。詳細はこちら: https://issues.Apache.org/jira/browse/WICKET-1767 。
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"/>
スプリングを使用する場合は、SessionFixationProtectionStrategy
を使用する必要があります。
<property name="sessionAuthenticationStrategy" ref="sas"/>
...
<bean id="sas" class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy"/>
ソースコード を調べると、これはharsha89のアプローチに似ていることがわかります。
JSESSIONIDがブラウザに表示されるのか、それともCookieに設定されるのかという問題はありますか?あなたの場合は後者だと思います。
1.ログイン後に新しいJSESSIONID Cookieを発行する
これは、Tomcatのデフォルトの動作ですhttpからhttpsに切り替えた場合ログイン時。古いものは破棄され、新しいものが生成されます。
ログイン自体がhttpを介している場合、それは監査人にとって別のセキュリティ問題だと思います;)
または、すべてのページがhttps経由ですか?
HttpServletRequest.changeSessionId()
を使用して、いつでもセッションIDを変更できます。
Tomcatを使用しており、Tomcatの認証メカニズムを使用するすべてのサーブレットにこれをグローバルに適用する場合、この サンプルコード に示すように、この動作を強制するバルブを記述できます。
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;
}