【Sevlet/JSP】セッション管理とCookieの扱い

2025-08-04

はじめに

Webアプリケーション開発において、セッション管理Cookieの扱いは非常に重要な概念です。HTTPプロトコルは本来ステートレス(状態を保持しない)なプロトコルですが、実際のWebアプリケーションではユーザーの状態を保持する必要が頻繁に発生します。この記事では、Servletを中心に、セッション管理とCookieの適切な使用方法について詳しく解説します。

セッション管理の基本概念

なぜセッション管理が必要か?

ECサイトでの買い物かご機能や、ログイン状態の保持など、Webアプリケーションではユーザーにまたがる情報を保持する必要があります。HTTP自体はステートレスなプロトコルなので、これを実現するための仕組みが必要です。

セッション管理の主要な方法

  1. Cookieを使用する方法
  2. URLリライティング
  3. 隠しフィールド(hidden field)
  4. HttpSessionオブジェクト

この記事では、特にCookieとHttpSessionに焦点を当てて解説します。

Cookieの基礎知識

Cookieとは?

Cookieは、サーバーがクライアント(ブラウザ)に小さなデータを保存させ、後で再利用するための仕組みです。主に以下の目的で使用されます:

  • セッション管理(セッションIDの保持)
  • パーソナライゼーション(ユーザー設定の保持)
  • トラッキング(ユーザー行動の分析)

Cookieの特徴

  • 名前と値のペアで構成される
  • 有効期限を設定できる
  • ドメインとパスごとに管理される
  • セキュリティ制約を設定できる(Secure属性、HttpOnly属性など)
  • 一般的に1つのCookieは4KBまでのサイズ制限がある
  • 1ドメインあたり20~50個程度の制限(ブラウザ依存)

ServletでのCookie操作

Cookieの作成と送信

// Cookieの作成
Cookie userCookie = new Cookie("username", "taro_yamada");
// 有効期限を設定(秒単位、ここでは1日)
userCookie.setMaxAge(60 * 60 * 24);
// Cookieをレスポンスに追加
response.addCookie(userCookie);

クライアントから送信されたCookieの読み取り

// リクエストからCookieを取得
Cookie[] cookies = request.getCookies();
String username = null;

if (cookies != null) {
    for (Cookie cookie : cookies) {
        if ("username".equals(cookie.getName())) {
            username = cookie.getValue();
            break;
        }
    }
}
if (username != null) { response.getWriter().println(“Welcome back, ” + username + “!”); } else { response.getWriter().println(“Welcome, new user!”); }

Cookieの削除

// 削除したいCookieと同じ名前のCookieを作成
Cookie deleteCookie = new Cookie("username", "");
// 有効期限を0に設定
deleteCookie.setMaxAge(0);
// パスも元のCookieと一致させる必要がある
deleteCookie.setPath("/");
response.addCookie(deleteCookie);

HttpSessionの基礎知識

HttpSessionとは?

HttpSessionは、サーバー側でユーザーごとのセッション情報を保持するためのインターフェースです。Servletコンテナ(Tomcatなど)が提供する機能で、以下の特徴があります:

  • サーバー側でセッション情報を管理
  • セッションIDを使ってクライアントと紐付け
  • デフォルトではCookieを使用してセッションIDを管理
  • 一定時間アクセスがないと自動的に無効化

セッションのライフサイクル

  1. 作成request.getSession()が初めて呼ばれた時
  2. 使用:セッション属性の設定・取得
  3. 無効化
  • 明示的にinvalidate()が呼ばれた時
  • タイムアウトした時
  • サーバーが停止した時

ServletでのHttpSession操作

セッションの取得

// セッションを取得(存在しない場合は新規作成)
HttpSession session = request.getSession();

// セッションを取得(存在しない場合はnullを返す)
HttpSession session = request.getSession(false);

セッションへのデータ格納

// セッションにデータを格納
session.setAttribute("user", userObject);
session.setAttribute("cart", shoppingCart);

セッションからのデータ取得

// セッションからデータを取得
User user = (User) session.getAttribute("user");
ShoppingCart cart = (ShoppingCart) session.getAttribute("cart");

// 存在しない場合はnullが返る
if (user != null) {
    // ユーザーがログイン済みの処理
}

セッションの無効化

// セッションを無効化(ログアウト処理など)
session.invalidate();

セッションタイムアウトの設定

// セッションのタイムアウトを秒単位で設定(30分)
session.setMaxInactiveInterval(30 * 60);

// web.xmlで全セッションのデフォルトタイムアウトを設定
<session-config>
    <session-timeout>30</session-timeout>
</session-config>

セッションIDの仕組み

セッションIDの生成と管理

  1. サーバーがセッションを作成すると、一意のセッションIDが生成される
  2. このセッションIDは通常、JSESSIONIDという名前のCookieとしてクライアントに送信される
  3. クライアントは以降のリクエストでこのCookieを送信
  4. サーバーは送信されたセッションIDをもとに適切なセッションを取得

Cookieが無効な場合の対応(URLリライティング)

// すべてのURLにセッションIDを自動的に埋め込む
String encodedURL = response.encodeURL("/some/path");
out.println("Click here");

セキュリティに関する考慮事項

セッション固定化攻撃(Session Fixation)対策

// 重要な権限変更前にセッションIDを変更
if (loginSuccessful) {
    HttpSession oldSession = request.getSession();
    // すべてのセッション属性を保持
    Map attributes = new HashMap<>();
    Enumeration names = oldSession.getAttributeNames();
    while (names.hasMoreElements()) {
        String name = names.nextElement();
        attributes.put(name, oldSession.getAttribute(name));
    }
    // 古いセッションを無効化
    oldSession.invalidate();
    // 新しいセッションを作成
    HttpSession newSession = request.getSession(true);
    // 属性を新しいセッションにコピー
    for (Map.Entry entry : attributes.entrySet()) {
        newSession.setAttribute(entry.getKey(), entry.getValue());
    }
}

安全なCookie設定

Cookie sessionCookie = new Cookie("JSESSIONID", session.getId());
// Secure属性(HTTPSのみで送信)
sessionCookie.setSecure(true);
// HttpOnly属性(JavaScriptからアクセス不可)
sessionCookie.setHttpOnly(true);
// SameSite属性(CSRF対策)
response.setHeader("Set-Cookie", "JSESSIONID=" + session.getId() + "; Secure; HttpOnly; SameSite=Lax");

実践的な使用例

ログインシステムの実装例

LoginServlet.java

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        // 簡易的な認証(実際はデータベースなどで確認)
        if ("admin".equals(username) && "password123".equals(password)) {
            HttpSession session = request.getSession();
            session.setAttribute("user", username);
            session.setAttribute("loginTime", new Date());

            // 「ログイン状態を保持する」チェックボックスがオンなら長期Cookieを設定
            if ("on".equals(request.getParameter("remember"))) {
                Cookie rememberCookie = new Cookie("rememberUser", username);
                rememberCookie.setMaxAge(30 * 24 * 60 * 60); // 30日間
                rememberCookie.setPath("/");
                rememberCookie.setHttpOnly(true);
                response.addCookie(rememberCookie);
            }

            response.sendRedirect("welcome");
        } else {
            request.setAttribute("error", "Invalid username or password");
            request.getRequestDispatcher("/login.jsp").forward(request, response);
        }
    }
}

LogoutServlet.java

@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // セッションを無効化
        HttpSession session = request.getSession(false);
        if (session != null) {
            session.invalidate();
        }

        // ログイン状態保持用Cookieを削除
        Cookie deleteCookie = new Cookie("rememberUser", "");
        deleteCookie.setMaxAge(0);
        deleteCookie.setPath("/");
        response.addCookie(deleteCookie);

        response.sendRedirect("login.jsp");
    }
}

WelcomeServlet.java

@WebServlet("/welcome")
public class WelcomeServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        HttpSession session = request.getSession(false);

        if (session == null || session.getAttribute("user") == null) {
            response.sendRedirect("login.jsp");
            return;
        }

        String username = (String) session.getAttribute("user");
        Date loginTime = (Date) session.getAttribute("loginTime");

        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("Welcome");
        out.println("

Welcome, " + username + "!

"); out.println("

You logged in at: " + loginTime + "

"); out.println("Logout"); out.println(""); } }

パフォーマンスとスケーラビリティの考慮

セッションサイズの最適化

  • セッションに格納するデータは必要最小限に
  • 大きなオブジェクトはセッションに格納しない
  • 定期的に不要な属性を削除
// 不要なセッション属性を削除
session.removeAttribute("temporaryData");

分散環境でのセッション管理

  • セッションの永続化(データベースやRedisなど)
  • セッションの複製(Tomcatクラスタリング)
  • ステートレスな設計の検討(JWTなど)

ベストプラクティス

  1. セッションの使用は必要最小限に:必要な場合のみ使用し、可能ならリクエスト属性やアプリケーションスコープを検討
  2. 機密情報はCookieに保存しない:パスワードやクレジットカード情報などは絶対にCookieに保存しない
  3. 適切なタイムアウト設定:アプリケーションの性質に応じて適切なセッションタイムアウトを設定
  4. セキュリティ属性の設定:Secure、HttpOnly、SameSite属性を適切に設定
  5. セッション固定化攻撃への対策:権限昇格時にセッションIDを変更

よくある問題と解決策

問題1:セッションが予期せず失われる

原因

  • サーバーの再起動
  • タイムアウト
  • クライアント側でCookieが拒否されている

解決策

  • セッションタイムアウトを適切に設定
  • Cookieが有効か確認するJavaScriptを追加
  • 重要な操作の前にセッションが有効か確認

問題2:複数タブで操作するとデータが上書きされる

原因

  • 同じセッションを複数タブで共有しているため

解決策

  • タブごとに異なる識別子を使用
  • 操作対象のデータをリクエストパラメータで渡す

問題3:セッションサイズが大きくなりパフォーマンスが低下

原因

  • セッションに大きなオブジェクトを格納している

解決策

  • セッションに格納するデータ量を削減
  • 大きなデータはデータベースに保存し、IDのみをセッションに保持

まとめ

セッション管理とCookieの扱いは、Webアプリケーション開発において不可欠な技術です。Servletを使用したセッション管理では、HttpSessionインターフェースを中心に、サーバー側でユーザー状態を効率的に管理できます。一方、Cookieはクライアント側に小さなデータを保持する手段として、セッションIDの保持やユーザー設定の保存などに利用されます。

適切なセッション管理を行うことで、ユーザーフレンドリーで安全なWebアプリケーションを構築できます。ただし、セキュリティやパフォーマンスに関する考慮を怠ると、重大な問題を引き起こす可能性があるため、常にベストプラクティスに従うことが重要です。

実際の開発では、ここで学んだ基本を踏まえ、アプリケーションの要件に応じて最適なセッション管理方法を選択してください。また、より高度なセッション管理技術(分散セッション、JWTなど)についても、必要に応じて学習を進めると良いでしょう。