何をしたいか
リバース プロキシで TLS を終端する構成でも、X-Forwarded-Proto を見て
「HTTPS で来ている」と判断できるようにする。
#548(コンテナ化)を検討する中で見つかった。
コンテナや Kubernetes では前段で TLS を終端し、アプリへは HTTP で転送するのが定石だが、
現状のフレームワークはその構成を想定していない。
なぜ必要か
#536 で入れた Cookie の Secure 属性が、この構成では立たない。
#536 は「HTTPS のときだけ Secure を立てる」という形にした。
ところが TLS を前段で終端すると、アプリから見た接続は HTTP なので
利用者のブラウザは HTTPS で繋いでいるのに、Secure が付かない。
セキュリティ対応を入れたつもりで、最も本番らしい構成でだけ効かない、という状態になっている。
現象(実測)
MVC_Sample (net10.0) に X-Forwarded-Proto: https を付けて要求しても、
応答の Cookie に secure は付かない。
[素の HTTP] .AspNetCore.Cookies=<値>; path=/; samesite=strict; httponly
[X-Forwarded-Proto: https] .AspNetCore.Cookies=<値>; path=/; samesite=strict; httponly
調べたこと
該当箇所は 4 つ(FxCmnFunction.cs のみ)
スキームで分岐しているのはこの 4 か所だけで、いずれも
セッション タイムアウト検出用 Cookie の生成・削除である。
| 行 |
対象 |
実装 |
Framework/Util/FxCmnFunction.cs:971 |
net10.0 |
cookieOptions.Secure = MyHttpContext.Current.Request.IsHttps; |
Framework/Util/FxCmnFunction.cs:1010 |
net10.0 |
同上 |
Framework/Util/FxCmnFunction.cs:1049 |
net48 |
newCookie.Secure = HttpContext.Current.Request.IsSecureConnection; |
Framework/Util/FxCmnFunction.cs:1088 |
net48 |
同上 |
net48 / net10.0 の両方が対象。 条件コンパイル(#if (NETSTD || NETCOREAPP))で
実装は分かれているが、どちらもプロキシの転送を見ていない点は同じ。
UseForwardedHeaders はリポジトリのどこにも無い
$ grep -rn 'UseForwardedHeaders' --include=*.cs .
(該当なし)
X-Forwarded-For は Framework/Util/GetClientIpAddress.cs と
Business/Presentation/MyBaseAsyncApiController.cs がヘッダを直読みしているため、
クライアント IP の取得は動く。X-Forwarded-Proto だけが誰にも見られていない。
帰結は 3 つ
#536 の Secure 自動設定が効かない(上記)
appSettings__CookieSecurePolicy=always(#541)が、この構成では実質必須になる。
あちらは IsHttps を見ずに明示的に立てるため、迂回策として機能する
UseHttpsRedirection を on にすると無限リダイレクトになる。
アプリが「HTTP で来た」と判断して HTTPS へ 307 → プロキシが HTTP で転送 → 繰り返し。
#541 で「前段で終端するなら off」と書いた根拠がこれ
案
net10.0 側 : UseForwardedHeaders
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedFor
});
パイプラインの先頭近く(UseHttpsRedirection より前)に置く必要がある。
踏みやすい罠 : KnownProxies / KnownNetworks を設定しないと黙って無視される。
既定ではループバックからの転送しか信用しない。
コンテナや Kubernetes では前段が別アドレスになるため、設定しないと何も起きない。
逆に無条件に信用すると、クライアントがヘッダを詐称できる(Secure の判断を
外から操作されることになる)ので、どちらにも倒せない。ここは慎重に決めたい。
置き場所も選択肢がある。
|
利点 |
欠点 |
サンプル(Startup.cs)に置く |
フレームワークの挙動を変えない |
利用者が自分で書く必要がある |
| フレームワーク側で面倒を見る |
何もしなくても正しく動く |
**既定の挙動が変わる。**下位互換の検討が要る |
**まずはサンプル側で、設定で on/off できる形(#541 と同じ流儀)**が穏当だと思う。
net48 側 : IIS の設定
net48 は HttpContext.Current.Request.IsSecureConnection で、
コードからは変えられない(読み取り専用)。
IIS + ARR なら URL Rewrite で HTTP_X_FORWARDED_PROTO を見て
サーバ変数 HTTPS を立てる、という定番の対処がある。
コードではなく Web.config と手順書の話になるので、
Samples/WebApp_sample/MVC_Sample の Web.config にコメントを置く形を想定している
(#541 で machineKey について同じことをした)。
影響
- 利用者への影響: 案(サンプル側・既定 off)なら無し。
フレームワーク側で面倒を見る案を採る場合は、既定の挙動が変わるため
下位互換の検討が要る
- net48 / net10.0: 両方。ただし対処が異なる
(net10.0 はコード、net48 は IIS の設定)
- 深刻度は高くない。
Secure が付かない対象はセッション タイムアウト検出用 Cookie で、
値は Environment.TickCount である(資格情報ではない)。
認証 Cookie 自体は ASP.NET Core の Cookie 認証 / FormsAuthentication 側の管轄で、
そちらは #541 の CookieSecurePolicy と requireSSL(#536)で立てられる
- 検証:
3_SmokeTest.ps1 は素の HTTP で回るため、既定 off なら影響を受けない
関連
#536 — Cookie の Secure を IsHttps で立てる形にした(本件の前提)
#541 — CookieSecurePolicy / UseHttpsRedirection を設定で切り替える形にした
#548 — コンテナ化。この構成を検討する中で見つかった
何をしたいか
リバース プロキシで TLS を終端する構成でも、
X-Forwarded-Protoを見て「HTTPS で来ている」と判断できるようにする。
#548(コンテナ化)を検討する中で見つかった。コンテナや Kubernetes では前段で TLS を終端し、アプリへは HTTP で転送するのが定石だが、
現状のフレームワークはその構成を想定していない。
なぜ必要か
#536で入れた Cookie のSecure属性が、この構成では立たない。#536は「HTTPS のときだけSecureを立てる」という形にした。ところが TLS を前段で終端すると、アプリから見た接続は HTTP なので
利用者のブラウザは HTTPS で繋いでいるのに、
Secureが付かない。セキュリティ対応を入れたつもりで、最も本番らしい構成でだけ効かない、という状態になっている。
現象(実測)
MVC_Sample (net10.0)にX-Forwarded-Proto: httpsを付けて要求しても、応答の Cookie に
secureは付かない。調べたこと
該当箇所は 4 つ(
FxCmnFunction.csのみ)スキームで分岐しているのはこの 4 か所だけで、いずれも
セッション タイムアウト検出用 Cookie の生成・削除である。
Framework/Util/FxCmnFunction.cs:971cookieOptions.Secure = MyHttpContext.Current.Request.IsHttps;Framework/Util/FxCmnFunction.cs:1010Framework/Util/FxCmnFunction.cs:1049newCookie.Secure = HttpContext.Current.Request.IsSecureConnection;Framework/Util/FxCmnFunction.cs:1088net48 / net10.0 の両方が対象。 条件コンパイル(
#if (NETSTD || NETCOREAPP))で実装は分かれているが、どちらもプロキシの転送を見ていない点は同じ。
UseForwardedHeadersはリポジトリのどこにも無いX-Forwarded-ForはFramework/Util/GetClientIpAddress.csとBusiness/Presentation/MyBaseAsyncApiController.csがヘッダを直読みしているため、クライアント IP の取得は動く。
X-Forwarded-Protoだけが誰にも見られていない。帰結は 3 つ
#536のSecure自動設定が効かない(上記)appSettings__CookieSecurePolicy=always(#541)が、この構成では実質必須になる。あちらは
IsHttpsを見ずに明示的に立てるため、迂回策として機能するUseHttpsRedirectionを on にすると無限リダイレクトになる。アプリが「HTTP で来た」と判断して HTTPS へ 307 → プロキシが HTTP で転送 → 繰り返し。
#541で「前段で終端するなら off」と書いた根拠がこれ案
net10.0 側 :
UseForwardedHeadersパイプラインの先頭近く(
UseHttpsRedirectionより前)に置く必要がある。置き場所も選択肢がある。
Startup.cs)に置く**まずはサンプル側で、設定で on/off できる形(
#541と同じ流儀)**が穏当だと思う。net48 側 : IIS の設定
net48 は
HttpContext.Current.Request.IsSecureConnectionで、コードからは変えられない(読み取り専用)。
IIS + ARR なら URL Rewrite で
HTTP_X_FORWARDED_PROTOを見てサーバ変数
HTTPSを立てる、という定番の対処がある。コードではなく
Web.configと手順書の話になるので、Samples/WebApp_sample/MVC_SampleのWeb.configにコメントを置く形を想定している(
#541でmachineKeyについて同じことをした)。影響
フレームワーク側で面倒を見る案を採る場合は、既定の挙動が変わるため
下位互換の検討が要る
(net10.0 はコード、net48 は IIS の設定)
Secureが付かない対象はセッション タイムアウト検出用 Cookie で、値は
Environment.TickCountである(資格情報ではない)。認証 Cookie 自体は ASP.NET Core の Cookie 認証 / FormsAuthentication 側の管轄で、
そちらは
#541のCookieSecurePolicyとrequireSSL(#536)で立てられる3_SmokeTest.ps1は素の HTTP で回るため、既定 off なら影響を受けない関連
#536— Cookie のSecureをIsHttpsで立てる形にした(本件の前提)#541—CookieSecurePolicy/UseHttpsRedirectionを設定で切り替える形にした#548— コンテナ化。この構成を検討する中で見つかった