LoginSignup
28
30

More than 5 years have passed since last update.

【社内勉強会】Sessionの基礎(2017/09/28)

Last updated at Posted at 2017-09-28
1 / 37

0. はじめに


前提知識

  • HTTP(リクエスト、レスポンス、ヘッダ、ボディ)
  • Servlet, JSP

参考図書


目次

  1. HTTPはステートレスプロトコル
  2. Cookie
  3. セッションとは
  4. Servletでセッションの作成

環境構築

  • Tomcat8(Servlet, JSP)
  • Java8
  • プロジェクト名: "SampleWeb"
  • Firefox55

1. HTTPはステートレスプロトコル


[復習] HTTP通信の流れ

image
HTTP はステートレス 引用

  1. クライアントがサーバにリクエストを送る
  2. サーバがリクエストを受け取る
  3. サーバがクライアントにレスポンスを返す
  4. クライアントがレスポンス(HTMLなど)を表示する

HTTPはステートレスプロトコル

  • HTTPは1回のやりとり(リクエスト、レスポンス)で処理が完結しているので、「状態」を管理できない

  • ステートレスとは「サーバがクライアントのアプリケーション状態を保存しない」制約のこと。

  • ステートレスの逆は「ステートフル」

HTTP はステートレス 参考


ステートフルなやりとり(ハンバーガショップにて)

店員「いらっしゃい」
客「ハンバーガーセット1つ」
店員「OK サイドメニューはどうする?」
客「ポテトMで」
店員「OK ドリンクはどうする?」
客「コーラMで」
店員「OK かしこまりました」

「Webを支える技術」を読みました とRESTのまとめ 引用


ステートレスなやりとり(ハンバーガショップにて)

店員「いらっしゃい」
客「ハンバーガーセットで」
店員「OK サイドメニューはどうする?」
客「ハンバーガーセットのポテトMで」
店員「OK ドリンクはどうする?」
客「ハンバーガーセットのポテトMとコーラMで」
店員「OK かしこまりました」

「Webを支える技術」を読みました とRESTのまとめ 引用


ステートレス、ステートフルなやりとりの特徴

  • ステートフルなやりとりは、

    • 簡潔
    • サーバがクライアントのそれまでの注文を覚えている
  • ステートレスなやりとりは、

    • 冗長
    • クライアントは毎回すべての注文を繰り返している

※『Webを支える技術』P082 引用


[補足] FTPはステートフルなプロトコル

FTPは、ログインしてからログアウトするまで、そのクライアントがどのディレクトリにいるかといった、アプリケーション状態をサーバが管理する。

※『Webを支える技術』P082 参考


2. HTTP Cookie


ステートレスプロトコルは不便

  • ログイン状態など保持できない
    • ネットショッピングができない

⇒「Cookie」という仕組みで、疑似的に「ステートフル」を実現した


Cookieとは?

Cookie は、ステートレスな HTTP プロトコルのためにステートフルな情報を記憶したもの。

Cookie は主に、以下の 3 つの用途で使用される。

  • セッション管理 (ユーザーのログイン、ショッピングカート)
  • 個人設定 (ユーザーの設定)
  • トラッキング (ユーザーの行動を分析する)

HTTP Cookie - MDN 引用


Cookieの具体的な流れ

  1. サーバーはクライアントへ、Cookie を保存するよう指定する( Set-Cookieヘッダ使用)
レスポンスヘッダ
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
...
  1. クライアント(ブラウザ)はCookieを保存する。

  2. クライアントはサーバにリクエストを送るときに、CookieヘッダでCookieを送る。

リクエストヘッダ
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry

HTTP Cookie - MDN 参考


[Try] ServletでCookieを確認

Cookieを保存させるServletを作成

Cookie.java
@WebServlet(urlPatterns = { "/Cookie" })
public class Cookie extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setHeader("Set-Cookie", "yummy_cookie=choco");
        PrintWriter out = response.getWriter();
        out.println("/Cookie");
    }
}

レスポンスヘッダのSet-Cookieを確認

  1. http://localhost:8080/SampleWeb/Cookie にアクセス
  2. レスポンスヘッダのSet-Cookieを確認
    image

  3. ブラウザでCookieを確認
    image


リクエストヘッダのCookieを確認

  1. プロジェクト配下のURLにアクセス( http://localhost:8080/SampleWeb/Hello

image


3. セッション


Cookieのみでログイン状態を維持できそうだけど…

下記の情報をCookieに保存すれば、ログイン状態を維持できそう…

  • ユーザID
  • パスワード
  • ユーザ名
  • 住所
  • 電話番号

Cookieのみでログイン状態を維持したときの問題

  • Cookieは確実に保存される訳ではない
    • シークレットモード
    • ユーザがブラウザの履歴を削除する
  • 最大容量が4KBで小さい
  • デフォルト(secureを付与しなければ)は平文で送るため、セキュリティに問題あり

※ 『Real World HTTP』P40 参考

Cookie の仕組み全体が本質的に安全ではないため、HTTP Cookie で機密情報を保存したり送信したりしてはいけません。

HTTP Cookie - MDN 引用


[補足] IPA試験対策:Cookieにsecure属性をつける

https: のページで使用するCookieには発行時に secure属性をつける。
secure属性が付けられたCookieは、http:のコンテンツ宛には送られない。
http: のページにおいてもCookieを使用する必要がある場合は、Cookie の値を第三者が傍受し得ることを十分に考慮した上で secure属性のつかない Cookieを発行する。

セッション対策 - セキュアプログラミング講座 引用


セッションとは?

本スライドでの「セッション」の意味:

ログインしてからログアウトするまでの一連の操作


先の問題を解決する対応案

  • Cookieにはユーザを認識できるID(セッションID)のみを保存
  • ユーザ名などの個人情報はサーバで管理

Cookieに個人情報は保存していないので、先の例よりセキュリティ的に良い(完全に問題が解決された訳ではない。後述のセッションハイジャック参照)


[Try] Servletでセッションを作成

request.getSession(true);でセッションを作成する。

CreateSession.java
@WebServlet(urlPatterns = { "/CreateSession" })
public class CreateSession extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //Session取得。セッションがなければセッション作成。
        HttpSession session = request.getSession(true);
        PrintWriter out = response.getWriter();
        out.println("CreateSession");
        //セッションIDを表示
        out.println("session_id=" + session.getId());
    }

}

セッションIDを確認

ブラウザ表示結果
CreateSession
session_id=5F4F3DF78E1BC8822CE970D9B3E3ECF7

image

  • ServletのセッションIDは、32文字のランダムな値(0-9A-F)
  • Cookieに保存されたセッションIDのキーは、"JSESSIONID"

[Try] セッションハイジャックを確認

セッションハイジャックは、利用者が Webアプリケーションにログインした際に発行されるセッション ID を、ネットワーク上で盗聴されたり、規則性から類推されることで、攻撃者が利用者になりすます攻撃です。

セッション管理の不備と対策 引用


ログイン用のサーブレットを作成

Login.java
/** SessionにUser Nameを保存する */
@WebServlet(urlPatterns = { "/Login" })
public class Login extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");    
        //Sessionにユーザ名を設定
        HttpSession session = request.getSession(true);
        String userName = request.getParameter("userName");
        session.setAttribute("userName", userName);

        PrintWriter out = response.getWriter();
        out.println("Login Success");
    }

}
LoginForm.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<form action="/SampleWeb/Login" method="POST">
    User Name <input type="text" name="userName">
    <input type="submit" value="Login">
</form>
</body>
</html>
HelloUser.java
/** Sessionに保存したUser Nameを表示 */
@WebServlet("/HelloUser")
public class HelloUser extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // Sessionがなければnullを返す
        HttpSession session = request.getSession(false);

        PrintWriter out = response.getWriter();
        out.println("HelloUser");

        if (session == null) {
            out.println("Session Does Not Exist");
        } else {
            out.println("userName = " + session.getAttribute("userName"));
        }
    }
}

一般ユーザAの動作

  1. http://localhost:8080/SampleWeb/form.html にアクセス
  2. User Nameに"Hoge"を入力して、Login
    ⇒ SessionにUser Nameが保存される
  3. http://localhost:8080/SampleWeb/HelloUser にアクセス
    ⇒ User Name"Hoge"が表示される

攻撃者Xの動作(セッションハイジャック)

  1. ユーザA:ログインする
  2. 攻撃者X:ユーザAのセッションIDをなんらかの手段で取得する(盗聴など)
  3. 攻撃者X:Cookieに取得したセッションIDを保存する
  4. 攻撃者X:http://localhost:8080/SampleWeb/HelloUser
    ⇒ User Name "Hoge"が表示される

[補足] セッション固定化攻撃

セッションID固定化攻撃は、Webサイトが生成する正規のセッションIDを含むURLに利用者をアクセスさせ、攻撃者が用意したセッションIDを使用する通信を意図的に確立させる攻撃です。攻撃者はその後同じセッションIDでWebサイトにアクセスし、ログイン中のセッションを乗っ取ります。

たとえセッションIDに推測が困難な乱数を使用していたとしても、以下に挙げるWebサイトでは攻撃者に正規のセッションIDを取得され、セッション固定化攻撃を受ける可能性があります。

  • ログイン画面(URLなど)にセッションIDを表示する仕様になっており、ログイン後も同じセッションIDを使用する
  • クッキーを使用できない場合に、セッション情報をURLに埋め込むURLリライティング機能が有効になっている
  • セッションIDが利用者ごとに固定されているなど容易に推測が可能である

情報処理安全確保支援士 平成29年春期 午前Ⅱ 問5 引用


[補足] Cookieに情報を格納するのは昔のやり方

ローカルストレージ用に考えられた新しい API を使うべき。

Web storage API (localStorage, sessionStorage)

image
Web Storage 引用

// sessionStorage にデータを保存する
sessionStorage.setItem("yummy", "choco");
// sessionStorage に保存したデータを取得する
var data = sessionStorage.getItem("yummy");
// sessionStorage に保存したデータを削除する
sessionStorage.removeItem("yummy");

IndexedDB

IndexedDB は、ユーザのブラウザ内にデータを永続的に保存する手段です。ネットワークの状態にかかわらず高度な問い合わせ機能を持つ Web アプリケーションを作成できますので、オンラインとオフラインの両方で動作するアプリケーションになります。

IndexedDB を使用する 引用


[補足] HTTPクッキーの命名例

  • JSESSIONID (JSP)
  • PHPSESSID (PHP)
  • CGISESSID (CGI)
  • ASPSESSIONID (ASP)

名前からプログラム言語が推測できる(確定ではない)。


[補足] セッションタイムアウトを設定する方法

web.xml
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>

web.xml デプロイメント記述子の要素 参考


[補足] JSPにアクセスするとセッションが作成される。

jspにアクセスすると、Cookieに"JSESSIONID"が保存される。
これは、JSPからコンパイルされたServletのpageContext.getSession()によるものだと考えられる(未確認)。

hello_jsp.java
public void _jspService(/* ... */) {
  //...
    final javax.servlet.jsp.PageContext pageContext;
  //...

    try {
      response.setContentType("text/html; charset=UTF-8");
      pageContext = _jspxFactory.getPageContext(this, request, response,
                null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      //Sesssion 取得
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;
      ....
      }
}
28
30
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
30