こんにちは。またまたはまりましたので報告します。このところ連投だなあ(笑)
問題概要
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サーバを配置するケースが多いと思います。開発環境でオレオレ証明書でのテストが「何故か動かない」と、納期直前に泣きそうになった開発者の記録です。。。