読者です 読者をやめる 読者になる 読者になる

Webエンジニア susumuis の技術ブログ

このブログの内容は個人の見解であり、所属する組織の公式見解ではありません

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

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

問題概要

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を処理させている場合は、TomcatHTTPSかどうかを判断することが出来ず、常にHTTPだと認識して動作しているので、問題ありません。(リバースプロキシを使用していない場合)

しかし、もし、

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

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

取れる対策としては

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

などになると思います。

結論

AndroidTomcatは仲が悪い(・へ・)

2012/03/04 追記

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

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

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