1. 概要

このチュートリアルでは、SpringセキュリティによってHTTPセッションを制御する方法を説明します。

この制御は、セッションタイムアウトから、同時セッションおよびその他の高度なセキュリティ構成の有効化にまで及びます。

2. セッションはいつ作成されますか?

セッションがいつ作成されるか、およびSpringSecurityがセッションとどのように相互作用するかを正確に制御できます。

  • 常に–セッションがまだ存在しない場合は、常にセッションが作成されます。
  • ifRequired –セッションは、必要な場合にのみ作成されます(デフォルト)。
  • never –フレームワークはセッション自体を作成しませんが、セッションがすでに存在する場合はそれを使用します。
  • ステートレス–Springセキュリティによってセッションが作成または使用されることはありません。
<http create-session="ifRequired">...</http>

Javaの構成は次のとおりです。

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
}

この構成は、アプリケーション全体ではなく、Springセキュリティの機能のみを制御することを理解することが非常に重要です。 Springセキュリティは、セッションを作成しないように指示した場合、セッションを作成しませんが、アプリは作成する可能性があります。

デフォルトでは、 Springセキュリティは必要なときにセッションを作成します—これは「ifRequired」です。

よりステートレスなアプリケーションの場合、「 never 」オプションは、Springセキュリティ自体がセッションを作成しないことを保証します。 ただし、アプリケーションが作成する場合、Springセキュリティはそれを利用します。

最後に、最も厳密なセッション作成オプション「ステートレス」は、アプリケーションがセッションをまったく作成しないことを保証します。

これはSpring3.1で導入されたであり、Springセキュリティフィルターチェーンの一部(主に HttpSessionSecurityContextRepository SessionManagementFilter などのセッション関連部分)を効果的にスキップします。 ]およびRequestCacheFilter

これらのより厳密な制御メカニズムは、 Cookieが使用されないことを直接意味するため、すべてのリクエストを再認証する必要があります。

このステートレスアーキテクチャは、RESTAPIとそのステートレス制約でうまく機能します。 また、基本認証やダイジェスト認証などの認証メカニズムでもうまく機能します。

3. フードの下

認証プロセスを実行する前に、Springセキュリティはリクエスト間のセキュリティコンテキストの保存を担当するフィルターを実行します。 これはSecurityContextPersistenceFilterです。

コンテキストは、デフォルトで戦略 HttpSessionSecurityContextRepository に従って保存され、HTTPセッションをストレージとして使用します。

厳密なcreate-session=” stateless” 属性の場合、この戦略は別の戦略に置き換えられます— NullSecurityContextRepository —およびセッションは作成または使用されませんコンテキスト。

4. 同時セッション制御

すでに認証されているユーザーが再認証を試みると、アプリケーションはいくつかの方法のいずれかでそのイベントを処理できます。 ユーザーのアクティブなセッションを無効にして、新しいセッションでユーザーを再度認証するか、両方のセッションが同時に存在できるようにすることができます。

同時session-controlサポートを有効にする最初のステップは、web.xmlに次のリスナーを追加することです。

<listener>
    <listener-class>
      org.springframework.security.web.session.HttpSessionEventPublisher
    </listener-class>
</listener>

または、Beanとして定義することもできます。

@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
    return new HttpSessionEventPublisher();
}

これは、セッションが破棄されたときにSpringセキュリティセッションレジストリが通知されるようにするために不可欠です。

同じユーザーに対して複数の同時セッションを許可するために、 要素はXML構成で使用する必要があります。

<http ...>
    <session-management>
        <concurrency-control max-sessions="2" />
    </session-management>
</http>

または、Java構成を介してこれを行うことができます。

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.sessionManagement().maximumSessions(2)
}

5. セッションタイムアウト

5.1. セッションタイムアウトの処理

セッションがタイムアウトした後、ユーザーが期限切れのセッションID を使用してリクエストを送信すると、名前空間を介して構成可能なURLにリダイレクトされます。

<session-management>
    <concurrency-control expired-url="/sessionExpired.html" ... />
</session-management>

同様に、ユーザーが有効期限が切れていないが完全に無効のセッションIDでリクエストを送信した場合、それらも構成可能なURLにリダイレクトされます。

<session-management invalid-session-url="/invalidSession.html">
    ...
</session-management>

対応するJava構成は次のとおりです。

http.sessionManagement()
  .expiredUrl("/sessionExpired.html")
  .invalidSessionUrl("/invalidSession.html");

5.2. Spring Bootでセッションタイムアウトを設定する

プロパティを使用して、組み込みサーバーのセッションタイムアウト値を簡単に構成できます。

server.servlet.session.timeout=15m

期間の単位を指定しない場合、Springは秒と見なします。

簡単に言うと、この構成では、セッションは15分間非アクティブになると期限切れになります。 この期間を過ぎると、セッションは無効と見なされます。

Tomcatを使用するようにプロジェクトを構成した場合、セッションタイムアウトの精度は最低1分で、分単位でしかサポートされないことに注意する必要があります。 これは、たとえば 170s のタイムアウト値を指定すると、2分のタイムアウトになることを意味します。

最後に、 Spring Session はこの目的で同様のプロパティ( spring .session.timeout )をサポートしていますが、それが指定されていない場合、自動構成は最初に述べたプロパティの値へのフォールバック。

6. セッショントラッキングにURLパラメータを使用しないようにする

URLでセッション情報を公開すると、セキュリティリスクが増大します(OWASPトップ10リストの2007年の7位から2013年の2位へ)。

Spring3.0以降 、追加するURL書き換えロジック jsessionid URLを設定することで無効にできるようになりました disable-url-rewriting =” true” の中に名前空間。

または、サーブレット3.0以降、セッション追跡メカニズムをweb.xmlで構成することもできます。

<session-config>
     <tracking-mode>COOKIE</tracking-mode>
</session-config>

プログラム的に:

servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));

これにより、 JSESSIONIDをCookieまたはURLパラメーターのどこに保存するかが選択されます。

7. SpringSecurityによるセッション固定保護

フレームワークは、ユーザーが再度認証を試みたときに既存のセッションに何が起こるかを構成することにより、一般的なセッション固定攻撃に対する保護を提供します。

<session-management session-fixation-protection="migrateSession"> ...

対応するJava構成は次のとおりです。

http.sessionManagement()
  .sessionFixation().migrateSession()

デフォルトでは、Springセキュリティではこの保護が有効になっています(「 migrateSession 」)。 認証時に、新しいHTTPセッションが作成され、古いセッションが無効になり、古いセッションの属性がコピーされます。

これが希望どおりでない場合は、他に2つのオプションを使用できます。

  • none」が設定されている場合、元のセッションは無効になりません。
  • newSession」が設定されている場合、古いセッションの属性がコピーされることなく、クリーンなセッションが作成されます。

8. セキュアセッションCookie

次に、セッションCookieを保護する方法について説明します。

httpOnlyフラグとセキュアフラグを使用して、セッションCookieを保護できます

  • httpOnly :trueの場合、ブラウザスクリプトはCookieにアクセスできません
  • secure :trueの場合、CookieはHTTPS接続を介してのみ送信されます

web.xmlでセッションCookieにこれらのフラグを設定できます。

<session-config>
    <session-timeout>1</session-timeout>
    <cookie-config>
        <http-only>true</http-only>
        <secure>true</secure>
    </cookie-config>
</session-config>

この構成オプションは、Javaサーブレット3以降で使用できます。 デフォルトでは、 http-only はtrueであり、secureはfalseです。

対応するJava構成も見てみましょう。

public class MainWebAppInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext sc) throws ServletException {
        // ...
        sc.getSessionCookieConfig().setHttpOnly(true);        
        sc.getSessionCookieConfig().setSecure(true);        
    }
}

Spring Bootを使用している場合は、application.propertiesで次のフラグを設定できます。

server.servlet.session.cookie.http-only=true
server.servlet.session.cookie.secure=true

最後に、 Filter を使用して、これを手動で実現することもできます。

public class SessionFilter implements Filter {
    @Override
    public void doFilter(
      ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        Cookie[] allCookies = req.getCookies();
        if (allCookies != null) {
            Cookie session = 
              Arrays.stream(allCookies).filter(x -> x.getName().equals("JSESSIONID"))
                    .findFirst().orElse(null);

            if (session != null) {
                session.setHttpOnly(true);
                session.setSecure(true);
                res.addCookie(session);
            }
        }
        chain.doFilter(req, res);
    }
}

9. セッションでの作業

9.1. セッションスコープのBean

beanは、Webコンテキストで宣言されたbeanの@Scopeアノテーションを使用するだけで、sessionスコープで定義できます。

@Component
@Scope("session")
public class Foo { .. }

またはXMLの場合:

<bean id="foo" scope="session"/>

次に、beanを別のbeanに注入できます。

@Autowired
private Foo theFoo;

そして、Springは新しいBeanをHTTPセッションのライフサイクルにバインドします。

9.2. 生のセッションをコントローラーに注入する

生のHTTPセッションは、Controllerメソッドに直接挿入することもできます。

@RequestMapping(..)
public void fooMethod(HttpSession session) {
    session.setAttribute(Constants.FOO, new Foo());
    //...
    Foo foo = (Foo) session.getAttribute(Constants.FOO);
}

9.3. Rawセッションの取得

現在のHTTPセッションは、rawサーブレットAPIを介してプログラムで取得することもできます。

ServletRequestAttributes attr = (ServletRequestAttributes) 
    RequestContextHolder.currentRequestAttributes();
HttpSession session= attr.getRequest().getSession(true); // true == allow create

10. 結論

この記事では、Springセキュリティを使用したセッションの管理について説明しました。

また、Spring Referenceには、セッション管理に関する非常に優れたFAQが含まれています。

いつものように、この記事で紹介するコードは、GitHubから入手できます。 これはMavenベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。