アンドロイドは猫さんとは仲がわるいのか
こんにちは。またまたはまりましたので報告します。このところ連投だなあ(笑)
問題概要
Android端末で毎画面セッションIDが変わるという現象が開発用のローカル環境で発生し、延々と調べてしまいました。
原因は、http://d.hatena.ne.jp/s-ishigami/20110916/p1と同じで、セキュア属性付きのCookieをHTTPで変更できない件によるもので、それが、JSESSIONIDで発生していました。
原因
こういうことのようです。
「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); } } protected 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操作中にセッション切れになる場合も含めて)
このような性格のサービスの場合は注意が必要です。
取れる対策としては
などになると思います。
2012/03/04 追記
セッションハイジャック保護のため、SSLを使用したページではセキュアなセッションIDを発行すること自体は望ましいことです。IPAも推奨しています。しかし、httpとhttpsを行き来するケースでは、jsessionidを直接secureにして欲しくなく、別にセキュアなsessionidを投げて、httpで使用するjsessionidと紐付けを行うという実装方法が多く取られていると思います。
自分のアプリケーションはそのような構成になっていたのですが、まさかjsessionid自体をsecureにされるとは思っていませんでした。
実際はLBの後ろ側にAPサーバを配置するケースが多いと思います。開発環境でオレオレ証明書でのテストが「何故か動かない」と、納期直前に泣きそうになった開発者の記録です。。。