reverse proxyをHTTP/HTTPS両対応のサイトに導入するときの落とし穴

表題の落とし穴に落ちたので事例報告です。

HTTP/HTTPS両プロトコルで運用しているサイトにリバースプロキシを導入する場合、気をつけないと落とし穴にはまってしまうのでご注意ください。

何が問題なのか?

ブラウザがSSLでアクセスしたときに、バックエンドのアプリケーションサーバが
<script src="http://..." >
という"HTTP絶対URL"を含むHTMLを吐いてしまい、スクリプトが読み込まれないというバグ。

発生にいたるメカニズム

サーバ構成
フロントのリバースクプロキシにNginx、バックエンドのアプリケーションサーバにApacheを使用していました。
(ただ、これは何のソフトウェアを使っているかとかは関係なく、どの組み合わせでも起こりうると思います。)

フロントでNginxが80番と443番の両方のポートをListenして、バックエンドでApacheが8080番をListenします。
わりとよくある構成ではないかと思います。
ブラウザ -> (http)  -> Nginx(80)  -> (http) -> Apache(8080)
ブラウザ -> (https) -> Nginx(443) -> (http) -> Apache(8080)
Nginxは、クライアントから静的コンテンツを要求された場合は即レスポンスし、動的コンテンツ(.phpなど)を要求された場合は背後のApacheにリクエストを転送します。
このとき、NginxとApache間の通信はHTTPで行われます。
ここがハマリポイントです。

何が問題なのか?

バックエンドサーバが受けるリクエストが常にHTTPであるということです。
アプリケーションの作りが悪い場合、下記のような絶対URLのscript要素を吐いてしまうことがあります。
<script src="http://..." ></script>
このような作りになっていると、ブラウザがHTTPSでアクセスしてきたときにもHTTPのURLを吐いてしまい、スクリプトが読み込まれずバグになります。

もうこの時点で「URLは相対パスで書け」とか「ネットワークパス参照(network-path reference)」使え」という声が聞こえてきそうですね。
もちろん自分でアプリを書くときはそのようにします。

いい加減、<script src="http://.. と書くのはやめましょう

問題は他人が書いたアプリを運用するときとか、CMSを使っている場合です。
CMSやCMSプラグインの中には上記のような絶対URLを吐くものが割とよくある気がします。

対処法

いろいろ選択肢はあります。
その1:相対パスで書く
これが王道でしょう。
しかしアプリがCMSの場合、例えばWordPressのソースコードを改変したりプラグインを改修したりというのはなかなか大変です。
仮に改修できたとしてもアップグレードのときにまた同じ改修をしないといけなくなります。
その2:フロンとバックの間にもHTTPSを使う
ブラウザがHTTPで来たらバックもHTTPで、ブラウザがHTTPSで来たらバックもHTTPSで通信します。
ブラウザ -> (http)  -> Nginx(80)  -> (http) -> Apache(8080)
ブラウザ -> (https) -> Nginx(443) -> (https) -> Apache(444)
これならまあ動くかもしれません。
ただこれもデメリットがあって、

  • バックエンド側にもSSL証明書を組み込まないといけない
  • Nginx <-> Aapache間のSSLのオーバーヘッドがある
  • アプリ側が443番以外のHTTPSに対応してない場合がある。
実際、WordPressが443じゃないHTTPSだと動かなくて泣きました。
その3:常にsrc="https://..." を吐く
<script src="https://..." ></script>
この方法なら割と安全です。
デメリットは、SSL証明書の期限が切れたときにHTTPページも死んでしまうことでしょうか。

WordPressをこの方法で動かす方法は次の記事で紹介します。

まとめ

以上、リバースプロキシとHTTPS(SSL)の落とし穴の紹介でした。

私はまだリバースプロキシの経験が少ないので、何か間違っているとかもっとよい方法があるとかあれば、Twitterで教えていただけると幸いです。
@DQNEO
カテゴリ:

人気記事