Spring Securityでセッションを制御する
1. 概要
この記事では、Spring Security allows us to control our HTTP Sessionsがどのように機能するかを説明します。
この制御は、セッションタイムアウトから、同時セッションやその他の高度なセキュリティ設定の有効化にまで及びます。
参考文献:
Spring Securityログアウト
Springログアウトの例-ログアウトURL、logout-success-urlの構成方法、およびカスタムBeanを使用して高度なログアウトシナリオを処理する方法。
2. セッションはいつ作成されますか?
セッションを作成するタイミングと、Spring Securityがセッションと対話する方法を正確に制御できます。
-
always –セッションがまだ存在しない場合は、常にセッションが作成されます
-
ifRequired –セッションは必要な場合にのみ作成されます(default)
-
never –フレームワークはセッション自体を作成することはありませんが、すでに存在する場合はセッションを使用します
-
stateless – SpringSecurityによってセッションが作成または使用されることはありません
...
Java構成:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
}
アプリケーション全体ではなく、this configuration only controls what Spring Security doesを理解することが非常に重要です。 Spring Securityは、指示しないとセッションを作成しない場合がありますが、アプリはそうすることができます!
デフォルトでは、Spring Security will create a session when it needs one –これは「ifRequired」です。
a more stateless applicationの場合、「never」オプションは、SpringSecurity自体がセッションを作成しないことを保証します。ただし、アプリケーションが作成した場合、SpringSecurityはそれを利用します。
最後に、最も厳密なセッション作成オプション「stateless」はguarantee that the application will not create any session at allです。
これはSpring3.1ではintroducedであり、Spring Securityフィルターチェーンの一部(主にHttpSessionSecurityContextRepository、SessionManagementFilter、RequestCacheFilterなどのセッション関連部分)を効果的にスキップします。
これらのより厳密な制御メカニズムには、cookies are not used、つまりeach and every request needs to be re-authenticatedという直接的な意味があります。 このステートレスアーキテクチャは、REST APIとそのステートレス性の制約に適しています。 また、基本認証やダイジェスト認証などの認証メカニズムともうまく機能します。
3. フードの下
認証プロセスを実行する前に、Spring Securityは、リクエスト間のセキュリティコンテキスト(SecurityContextPersistenceFilter)の保存を担当するフィルターを実行します。 コンテキストは、HTTPセッションをストレージとして使用する戦略(デフォルトではHttpSessionSecurityContextRepository)に従って保存されます。
厳密なcreate-session=”stateless”属性の場合、この戦略は別の–NullSecurityContextRepository –およびno session will be created or usedに置き換えられ、コンテキストが維持されます。
4. 同時セッション制御
すでに認証されているユーザーがauthenticate againを試行すると、アプリケーションはいくつかの方法のいずれかでそのイベントを処理できます。 ユーザーのアクティブなセッションを無効にし、新しいセッションでユーザーを再度認証するか、両方のセッションが同時に存在することを許可します。
同時session-controlサポートを有効にする最初のステップは、web.xmlに次のリスナーを追加することです。
org.springframework.security.web.session.HttpSessionEventPublisher
または、次のようにBeanとして定義します。
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
これは、Spring Securityセッションレジストリがnotified when the session is destroyedであることを確認するために不可欠です。
同じユーザーに対して複数の同時セッションを許可するシナリオを有効にするには、XML構成で<session-management>要素を使用する必要があります。
または、Java構成を介して:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().maximumSessions(2)
}
5. セッションタイムアウト
5.1. セッションタイムアウトの処理
セッションがタイムアウトした後、ユーザーがexpired session idを使用してリクエストを送信すると、名前空間を介して構成可能なURLにリダイレクトされます。
同様に、ユーザーが有効期限が切れていないが完全にinvalidのセッションIDでリクエストを送信した場合、それらも構成可能なURLにリダイレクトされます。
...
対応する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でセッション情報を公開すると、security riskが増加します(OWASPトップ10リストの2007年の7位から2013年の2位へ)。
Starting with Spring 3.0、jsessionidをURLに追加するURL書き換えロジックは、<http>名前空間にdisable-url-rewriting=”true”を設定することで無効にできるようになりました。
または、サーブレット3.0以降、セッション追跡メカニズムをweb.xml:で構成することもできます。
COOKIE
そしてプログラム的に:
servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));
これにより、JSESSIONIDをCookieまたはURLパラメータに保存する場所が選択されます。
7. SpringSecurityによるセッション固定保護
このフレームワークは、ユーザーが再度認証を試みたときに既存のセッションに何が起こるかを構成することにより、典型的なセッション固定攻撃に対する保護を提供します。
...
対応するJava構成:
http.sessionManagement()
.sessionFixation().migrateSession()
デフォルトでは、Spring Securityではこの保護が有効になっています(「migrateSession」)。認証時に新しいHTTPセッションが作成され、古いセッションは無効になり、古いセッションの属性がコピーされます。
これが望ましい動作ではない場合、他の2つのオプションを使用できます。
-
「none」が設定されている場合、元のセッションは無効になりません
-
「newSession」が設定されている場合、古いセッションの属性がコピーされることなく、クリーンなセッションが作成されます。
8. Secure Session Cookie
次に、セッションCookieを保護する方法について説明します。
We can use the httpOnly and secure flags to secure our session cookie:
-
httpOnly: trueの場合、ブラウザスクリプトはCookieにアクセスできません
-
secure: trueの場合、CookieはHTTPS接続を介してのみ送信されます
セッションCookieにこれらのフラグをweb.xmlで設定できます。
1
true
true
この構成オプションは、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);
}
}
If we’re using Spring Boot, we can set these flags in our 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を別の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 Servlet APIを介してプログラムで取得することもできます。
ServletRequestAttributes attr = (ServletRequestAttributes)
RequestContextHolder.currentRequestAttributes();
HttpSession session= attr.getRequest().getSession(true); // true == allow create
10. 結論
この記事では、Spring Securityによるセッションの管理について説明しました。 また、Spring Referenceには非常に優れたFAQ on Session Managementが含まれています。
いつものように、この記事で紹介されているコードはover on Githubで利用できます。 これはMavenベースのプロジェクトであるため、インポートしてそのまま実行するのは簡単です。