encodeURLとencodeRedirectURL
このエントリはJava Advent Calendar 2011の23日目となります。
Servlet APIの一部であるHttpServletResponseのencodeURLとencodeRedirectURLメソッドについて、Java EEコンテナの一つであるWebLogic Server(WLS)での扱いについて紹介します。
基本動作
まず、encodeURLやencodeRedirectURLの基本動作としては、
- 指定したURLを、その中にセッションIDを含めてエンコードする
というだけです。例で示しましょう。
encodeURL("/test/foo?bar=baz") ⇒ /test/foo;jsessionid=4pQgTJ3LnvfTKp94272n1FfwRT2C7lsn3gxJyhVhbvL4syTn9Hdn!175414136?bar=baz
ここではjsessionidが付加されているの見て取れますが、Cookieに既にJSESSIONIDが含まれている場合はエンコードされず
encodeURL("/test/foo?bar=baz") ⇒ /test/foo?bar=baz
と引数に指定したURLがそのまま返るだけです。
上記まではencodeURLで示しましたが、encodeRedirectURLでも基本的に動作は同じです。
つまり、(昔のガラケーなど)Cookieをサポートしていないクライアントで、セッショントラッキングを実現させるために、URL中にセッションIDを含めるということになります。ここまではご存知の方も多いのではないでしょうか。
セキュリティ上の注意点
初回アクセス時はCookieが通常は存在しないため、上記APIを使うとURL中にセッションIDが含まれることになります。
URLにセッションIDが含まれてしまうと、Session HijackやSession Fixationを引き起こしやすくなるため、Cookieのみサポートする環境では、
- url-rewriting-enabledをfalseにする
方がよいでしょうし、さらにhttpsのみ利用する環境では
- cookie-secureをtrueにする
などの対策を行ったほうがよいでしょう。
参考: weblogic.xmlデプロイメント記述子の要素 - session-descriptor
encodeURLとencodeRedirectURLの違い
さて、上記の基本動作に挙げた例では、encodeURLとencodeRedirectURLの動作に違いはないように見えます。
では、なぜこの2つのメソッドが分かれているのでしょうか?まず、Java EEのAPIドキュメントを見てみましょう。
http://doc.java.sun.com/DocWeb/#r/Java%20EE%205/javax.servlet.http.HttpServletResponse/
まずは、encodeURLメソッドからです。
encodeURL
指定された URL を、その中にセッション ID を含めてエンコードします。または、エンコードが必要ない場合は、変更されていない URL を返します。このメソッドの実装には、セッション ID を URL 内でエンコードする必要があるかどうかを判断するロジックが含まれます。たとえば、ブラウザがクッキーをサポートする場合、またはセッションの追跡がオフにされている場合は、URL のエンコードは不要です。
セッション追跡を確実に行うには、サーブレットにより発行されたすべての URL が、このメソッドを通じて処理される必要があります。そうしないと、クッキーをサポートしないブラウザでは、URL の書き換えによるセッション管理を使用することはできません。
基本動作の項で説明した内容が十二分に記載されていますね。さすがAPIドキュメントです。
次に、encodeRedirectURLメソッドです。
encodeRedirectURL
sendRedirect メソッド内で使用するために、指定された URL をエンコードします。または、エンコーディングが必要ない場合は、変更しないで URL を返します。このメソッドの実装には、セッション ID を URL にエンコードする必要があるかどうかを判断するロジックが含まれています。この判断を行う規則が、通常のリンクをエンコードするかどうかの判断で使用する規則と異なる可能性があるため、このメソッドは encodeURL メソッドと分離されています。
HttpServletResponse.sendRedirect メソッドへ送られるすべての URL は、このメソッドを通じて処理される必要があります。そうしないと、クッキーをサポートしないブラウザで、URL の書き換えによるセッション管理を使用することはできません。
これを確認する限り、
- sendRedirectメソッド内で使用するためだけに分かれている
- コンテナ実装によって違いがありそうだが、詳細は分からない
となります。それでは、WLSの場合はどのようになっているのでしょう。
WLSは残念ながらオープンソース製品ではないため、基本的にはドキュメントから仕様を確認することになりますが、検索すると以下がヒットします。
プラグイン(Apache、NSAPI、ISAPI、HttpClusterServlet、またはHttpProxyServlet)を使用しており、バックエンド・サーバーでURL書換え(response.sendRedirect(url)またはresponse.encodeRedirectURL(url))を使用している場合は、一定の条件を満たすと、URLにPathTrimパラメータとPathPrependパラメータの両方が適用されます。一定の条件とは、「PathPrependがnullであるか、PathPrependがすでに適用されている」というもので、PathTrimはこのいずれかを満たす場合にのみ適用されます。
セッションとセッション永続性の使用 - URL書換えのコーディングに関するガイドライン
…よく、分からない日本語ですね*1。
ただ、Webサーバプラグインの PathTrim/PathPrepend パラメータに関係しそうということは何となく分かります。
Webサーバプラグインとは
別名プロキシプラグインとも呼ばれ、Webサーバに導入することで、WLSとの連係を行うためのものです。
また、PathTrim/PathPrepend パラメータはそれぞれ以下のような役割を持っています。
- PathTrim
- 特定のパスを先頭から削除してWLSに転送する
- PathPrepend
- 特定のパスを先頭に追加してWLSに転送する
パラメータ設定例 | Webサーバ上のURI | WLS上のURI |
---|---|---|
PathTrim=/weblogic | /weblogic/test/foo | /test/foo |
PathPrepend=/test | /foo | /test/foo |
PathTrim=/weblogic,PathPrepend=/test | /weblogic/foo | /test/foo |
のようになり、WebサーバとWLSのパス階層が何らかの理由により一致していない場合に、これを補正することができます。
参考: Webサーバー・プラグインの一般的なパラメータ
勘のよい方は、この辺で気づかれたかもしれませんが、実際にWLSでの動作を検証してみましょう。
以下の条件でテストしてみることにします。
- コンテキストパス=/test
- メソッドに渡す引数=/test/foo
パラメータ設定例 | encodeURL | encodeRedirectURL |
---|---|---|
PathTrim=/weblogic | /test/foo | /weblogic/test/foo |
PathPrepend=/test | /test/foo | /foo |
PathTrim=/weblogic,PathPrepend=/test | /test/foo | /weblogic/foo |
つまり、
- encodeURLの結果は「/test/foo」のまま変わっていない
- encodeRedirectURLの結果は、PathTrimやPathPrependの値に応じて変わる
ことになります。
言い換えると、encodeRedirectURLでラップしたURIをsendRedirectメソッドに渡すことで、WebサーバでPathTrimやPathPrependが設定されていても、クライアントに正しいリダイレクトレスポンス*2を返すことができるようになります。
サマリ
WLSのencodeRedirectURLは以下の条件を満たす場合、 リダイレクト用のURLを生成するために特殊な動作を行います。
結論としては
- sendRedirectメソッドに渡すための引数は、encodeRedirectURLでラップするようにしましょう!
- APIドキュメントはよく読んでおきましょう。
というお話でした。誰得なマニアックなネタですみません。。