概要
Spring Bootで動いているアプリケーションサーバー(以下APサーバー)が、Webページをリダイレクトするとき、リクエスト時のURLを元にリダイレクト先のURLを構築する。クライアントとAPサーバーの間にリバースプロキシサーバーがいる場合、APサーバーはリバースプロキシの作成したURLしか参照できないため、クライアントが必要とするリダイレクトURLを作成できない。この問題を解決するには、リバースプロキシがAPサーバーにリクエストを転送する際に、HTTPヘッダー「Forwarded」「X-Forwarded-」を付加し、クライアントからのリクエストURLで使われたスキーム(http or https)、ドメイン名、ポート番号などをAPサーバーに伝える。
課題
説明にあたり、まず、Spring Bootのリダイレクト処理について説明する。
Spring Bootのリダイレクト処理
Spring BootのリダイレクトURL作成のプロセスは以下である。
- Spring Boot内でリダイレクト処理があり、このときリダイレクト先として「/login」のようなルートからのパスが指定されたとする。
- すると、Spring BootはリクエストURLを参照し、ルートより前の部分、以下の例なら、「 http://example.com 」を補完する形でリダイレクトURLを作成する。
- 作成したリダイレクトURL「 http://example.com/login 」をクライアントに返却する。
リバースプロキシサーバーがいる場合
リバースプロキシサーバーは、通信の軽量化のため、以下のように、(リバースプロキシ ⇔ APサーバー)間の通信をSSLなしのHTTP通信で行っている。
ここでリダイレクト処理をしてみる。
Spring BootはリダイレクトURLの作成に当たり、直接参照できるURL、つまりリバースプロキシが作成したリクエストURLを参照する。リバースプロキシはHTTP通信でAPサーバーにアクセスするため、Spring BootはリダイレクトURLのスキームにSSLなしのHTTPプロトコルを指定する。すると、リダイレクト時に以下が起きる。
リダイレクトURLにはHTTPプロトコルを指定しているので、意図しないSSLなしのHTTP通信がインターネット上で発生してしまう。
原因
この問題の原因は、APサーバーがリダイレクトURLを作成する際に、(クライアント ⇔ リバースプロキシ)間で使われたURLを参照できないことにある。
解決策
この問題を解決するには、リバースプロキシがAPサーバーにリクエストを転送する際に、HTTPヘッダー「Forwarded」「X-Forwarded-〇〇」を付与し、APサーバーに(クライアント⇒リバースプロキシ)時のURL情報を伝える。
実装
リバースプロキシサーバーの設定
NGINXでのHTTPヘッダー付加の設定については省略する。
APサーバー「Spring Boot」の設定
以下のBeanを定義する。この設定により、Spring BootがリダイレクトURL作成時にリバースプロキシが送ってきたHTTPヘッダー「Forwarded」「X-Forwarded-〇〇」を参照するようになる。
@Bean
public ForwardedHeaderFilter forwardedHeaderFilter() {
return new ForwardedHeaderFilter();
}
なぜSpring BootはデフォルトでHTTPヘッダーを参照してくれないのか?
これは、HTTPヘッダーの偽装が容易で、セキュリティホールになり得るからである。そのため、Spring Bootでは、明示的に「Forwarded」「X-Forwarded-〇〇」の読み取りを指定する必要がある。
参考
リファレンス
- mdn web docs - 開発者向けのウェブ技術 > HTTP > HTTP ヘッダー > Forwarded
- SPRING FRAMEWORK - クラス ForwardedHeaderFilter
技術記事