SSLアクセラレータ使ってsendRedirect
とあるJavaで構築されているサイトで、HTTPS でアクセスさせたいのに、あるページにアクセスすると、HTTPになっちゃう事象発生。
あるページってのは、リダイレクトを使ってるページ。
原因は、SSLアクセラレータとアプリが上手く連携できるようなつくりになっていないこと。
SSLアクセラレータは、暗号化されたリクエスト(HTTPS)を、複合(HTTP)して、Webサーバに渡す。
クライアントではHTTPSでリクエストだけど、APサーバに届くときは、HTTPで届く状況。
リダイレクトをする場合、リダイレクト先のURLはレスポンスヘッダの"Location"に入れて返される。
そして、"Location"のURLは、絶対URI形式じゃないとならない。
そのため、APサーバ*1は、リダイレクト先のURLを以下のようにして作ろうとする。
- Webサーバが受け取ったリクエストがHTTPのときはhttp://?HTTPSのときはhttps://?
- リクエストヘッダの"Host"の値(HTTP1.1の場合)
- リダイレクト先として与えられたパス
そして今回問題となっているページでは、HttpServletResponse#sendRedirectが以下のように使われていた。
response.sendRedirect("/hoge/moge.jsp"); // 絶対URIではない。
今回の場合、クライアントからはHTTPSだが、APサーバに届くときはHTTPなので、作成される"Location"のURLは以下のようになる。
http://mogemoge.mogeoge/hoge/moge/jsp
これがそのままクライアントまで返ってリダイレクトがかかるので、https://〜〜だったはずなのに、http://〜〜になっちゃった〜。と。
これ、開発中にSSLアクセラレータが使えない環境だと何も問題にならないからヤラシイ。インフラがどうなってるか全く知らずに、アプリ作ってると不可避だろう。
# けど、リリース前に本番環境でテストしたら速攻気づくだろ。
# だって、途中から全然動かなくなるんだもん。
# テストしてないんだろうなぁ。。
これ最初から気づいてたとしたら、『APサーバにURLを作らせない』ようにしてアプリを作る必要がある。
例えば、Javaの場合、以下のような対応が考えられる。
- response.sendRedirectには、必ず絶対URLを与える。
- response.sendRedirect("https://〜〜");
- 画面内は、なるべく相対URL。
- 相対URLが無理な場合は、プロトコルやらホスト名は自前で作る。
これをやるとしたら、自前でプロトコルやらホスト名を取る仕組みと、それを開発〜リリースへと簡単に切り替えられるような仕組みを用意しとかないとならない。
仕組み作りは全然難しくないけど、開発後に対応するのはちとめんどい。
特になんかのフレームワークやら、Taglibやら使ってる場合、それがプロトコル・ホスト名を付け足してURLを作るようになっていると、そこを全部取り替える必要が出てくる。
アプリケーションのアーキテクチャの設計の前に、インフラがどうなっているかはちゃんと抑えようね。という一例。