リバースプロキシやロードバランサーで SSL offload を行った場合には、後ろにいる Web サーバーにクライアントの IP などを伝えるために X-Forwarded-*
を付けますが、ASP.NET は上手く扱ってくれません。
X-Forwarded-For
に関しては ARR Helper を入れることで対応できますが、X-Forwarded-Proto
は完全に未対応なので自力で何とかする必要があります。
ARR Helper がインストールされている場合には X-ARR-SSL
というヘッダーを渡してあげれば HTTPS = on
としてくれますが、非標準なヘッダーなので ARR と組み合わせた場合しか上手く動作しません。
HTTP モジュールで上書きする
ASP.NET の世界だけで扱えれば良いのであれば、専用のマネージ HTTP モジュールを用意して HTTPS / SERVER_PORT サーバー変数を書き換えてあげれば解決します。
適当に書いたコードですが、大体こんな感じになるのではないかと思います。
public class HttpForwardedModule : IHttpModule { public void Init(HttpApplication app) { app.BeginRequest += OnBeginRequest; } public void Dispose() { } private void OnBeginRequest(object sender, EventArgs e) { var app = (HttpApplication)sender; if (app.Context.Request.Headers["X-Forwarded-Proto"] == "https") { app.Context.Request.ServerVariables["HTTPS"] = "on"; app.Context.Request.ServerVariables["SERVER_PORT"] = "443"; } } }
このモジュールを PreApplicationStartMethod を使って IIS に登録してあげれば有効になります。
[assembly: PreApplicationStartMethod(typeof(WebApplication17.ModuleInitializer), "Start")] public class ModuleInitializer { public static void Start() { HttpApplication.RegisterModule(typeof(HttpForwardedModule)); } }
実際のところは BeginRequest のタイミングで上書きの処理が出来ればよいので、Global.asax でも同様に動作します。これで実行して X-Forwarded-Proto
付きのリクエストを投げてあげれば、ASP.NET は HTTPS なリクエストだと判断します。
URL Rewrite で上書きする
ASP.NET の世界で閉じていればマネージ HTTP モジュールで上手くいきますが、ASP.NET 側で変更したサーバー変数は IIS 側には反映されないので、後ろに PHP や HttpPlatformHandler が居る場合には使えません。
要はサーバー変数を書き換えてあげれば良いので、URL Rewrite でも同じことが実現できます。
<system.webServer> <rewrite> <!-- unlock が必要なので少し面倒 --> <allowedServerVariables> <add name="HTTPS" /> <add name="SERVER_PORT" /> </allowedServerVariables> <rules> <rule name="HttpForwarded"> <match url=".*" /> <conditions> <add input="{HTTP_X_FORWARDED_PROTO}" pattern="https" /> </conditions> <serverVariables> <set name="HTTPS" value="on" /> <set name="SERVER_PORT" value="443" /> </serverVariables> </rule> </rules> </rewrite> </system.webServer>
やっていることは HTTP モジュールと同じです。allowedServerVariables
はデフォルトではロックされているので、書き換えるには applicationHost.config を直接弄るか unlock してから追加する必要があります。
上の例ではアプリケーションの Web.config で書くことを想定してますが、globalRules に入れてマシン全体で使うことも出来るので、考えることが減って少し楽になるかも知れません。
リダイレクト時に https にならない問題
ARR の時には発生しないのですが、それ以外のリバースプロキシや LB を使っている場合には、URL Rewrite でリダイレクトを行うと http として返される問題があります。
何故 ARR の時には発生しないかというと、ARR にはレスポンスヘッダーを自動で書き換える機能があるからです。デフォルトで有効になっているので、ARR / ARR Helper / ASP.NET の場合は何も考えずに済みます。
しかし、それ以外の組み合わせになると一気に破綻するので、リダイレクトする際には https 付きの絶対 URL として URL Rewrite を書く必要があります。地味に面倒です。
Outbound Rule を使って一括で Location ヘッダーを書き換える方法も使えると思いますが、事故りやすいのであまりお勧めは出来ないと思ってます。
App Service を使う
例によって App Service を使う場合には ARR と ARR Helper という組み合わせになるので、いい感じに HTTPS = on
としてくれます。リダイレクト時の書き換え処理も走るので、ちゃんと https としてクライアントには返ります。
App Service で実行する場合には、特に必要な作業はありません。お手軽ですね。ちなみに App Service 以外でも ARR と ARR Helper の組み合わせであれば、同じように動作します。
根本的な対応は IIS が X-Forwarded-Proto
に対応するか、専用のネイティブ HTTP モジュールを書くしかないと考えてます。ARR Helper がもっと汎用的になって Proxy Helper とでもなれば最高なのですが。