アンドロイドは猫さんとは仲がわるいのか

こんにちは。またまたはまりましたので報告します。このところ連投だなあ(笑)

問題概要

Android端末で毎画面セッションIDが変わるという現象が開発用のローカル環境で発生し、延々と調べてしまいました。
原因は、http://d.hatena.ne.jp/s-ishigami/20110916/p1と同じで、セキュア属性付きのCookieをHTTPで変更できない件によるもので、それが、JSESSIONIDで発生していました。

<

div class="section">

原因

こういうことのようです。

「JSESSIONIDを保持したCookieをsecure属性にする方法」
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=5722&forum=12

hreq(HttpServletRequest) がSSLであれば、Secure属性を付けているようです。

セッションが初回(または無効)で、HTTPSでアクセスされたと判断すると、TomcatはSet-Cookieレスポンスヘッダをsecure属性付きで返します。この仕様は変えられないようです。これはこれで望ましいのですが困ってしまうことがあります。

以下は検証コードです。

public class MyServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        try {
            String path = request.getRequestURI().substring(request.getContextPath().length());
            if (path.equals("/cookie")) {
                request.getSession(true); // session start
                response.setContentType("text/plain; charset=UTF-8");
                PrintWriter writer = response.getWriter();
                writer.println("request cookies: ");
                if (request.getCookies() != null) {
                    for (Cookie reqCookie : request.getCookies()) {
                        writer.println("\\t" + cookieToString(reqCookie));
                    }
                }
            }
        } catch (Exception e) {
            throw new ServletException(e);
        }
    }
    prcted String cookieToString(Cookie cookie) {
    return cookie.getName() + "=" + cookie.getValue() + "\\n\\t\\t" +
    "d: " + cookie.getDomain() +
    ", p: " + cookie.getPath() +
    ", v: " + cookie.getVersion() +
    ", a: " + cookie.getMaxAge() +
    ", c: " + cookie.getComment() +
    ", s: " + cookie.getSecure();
    }
}


PCやiPhoneのブラウザでは、セキュア属性付きでセッションがスタートした直後にHTTPページへ遷移した場合、そのセッションが切れますが、新しいセッションが開始し、ブラウザもそのセッションIDをCookieで受け取ります。が、AndroidはこのCookieを拒否してしまいます。

解決方法

ロードバランサにSSLを処理させている場合は、TomcatがHTTPSかどうかを判断することが出来ず、常にHTTPだと認識して動作しているので、問題ありません。(リバースプロキシを使用していない場合)

しかし、もし、

  • LBやSSLアクセラレータを使っていない
  • AndroidでアクセスされるWebサービス
  • HTTPとHTTPSを行ったり来たりする
  • 「初回は必ずHTTPである」ことを保証できない(HTTPS操作中にセッション切れになる場合も含めて)

このような性格のサービスの場合は注意が必要です。

取れる対策としては

  • 全てSSLにする
  • セッションが無効で、HTTPSアクセスの場合は、HTTPにリダイレクトする
  • Cookieを使わない

などになると思います。

結論

AndroidとTomcatは仲が悪い(・へ・)

2012/03/04 追記

セッションハイジャック保護のため、SSLを使用したページではセキュアなセッションIDを発行すること自体は望ましいことです。IPAも推奨しています。しかし、httpとhttpsを行き来するケースでは、jsessionidを直接secureにして欲しくなく、別にセキュアなsessionidを投げて、httpで使用するjsessionidと紐付けを行うという実装方法が多く取られていると思います。

自分のアプリケーションはそのような構成になっていたのですが、まさかjsessionid自体をsecureにされるとは思っていませんでした。

実際はLBの後ろ側にAPサーバを配置するケースが多いと思います。開発環境でオレオレ証明書でのテストが「何故か動かない」と、納期直前に泣きそうになった開発者の記録です。。。

コメントを残す