From 236b2c352db400451cc10583847088edb6a9a017 Mon Sep 17 00:00:00 2001 From: Edouard Vanbelle Date: Sun, 19 Oct 2025 22:33:55 +0200 Subject: [PATCH 1/2] CoreHelpers: considers X-Forwarded-For header when behind a proxy * concerns https://github.com/bitwarden/server/issues/6467 --- src/Core/Utilities/CoreHelpers.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Core/Utilities/CoreHelpers.cs b/src/Core/Utilities/CoreHelpers.cs index 5acdc63489f4..d78900a642ff 100644 --- a/src/Core/Utilities/CoreHelpers.cs +++ b/src/Core/Utilities/CoreHelpers.cs @@ -644,6 +644,16 @@ public static string GetApplicationCacheServiceBusSubscriptionName(GlobalSetting { return realConnectingIp.ToString(); } + else if (globalSettings.SelfHosted && httpContext.Request.Headers.TryGetValue("X-Forwarded-For", out var xForwardedFor)) + { + // X-Forwarded-For is a de-facto standard + // RFC7239 normalizes into the Forwarded header (https://datatracker.ietf.org/doc/rfc7239/) + // it may replace the X-Forwarded-For header in the future + // Using a library should be more convenient to take benefit of both format and future evolutions + // + // Security: Considering here that Nginx has sanitized the header and there is no value forged from outside + return xForwardedFor.ToString().Split(",")[0]; + } return httpContext.Connection?.RemoteIpAddress?.ToString(); } From 81951df0e7e44fc5b30ebda5c94c74757a87f3f0 Mon Sep 17 00:00:00 2001 From: Edouard Vanbelle Date: Wed, 22 Oct 2025 00:46:50 +0200 Subject: [PATCH 2/2] Test X-Connecting-IP and X-Forwarded-For --- test/Core.Test/Context/CurrentContextTests.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/Core.Test/Context/CurrentContextTests.cs b/test/Core.Test/Context/CurrentContextTests.cs index b868d6ceaab5..95a0ac5e0631 100644 --- a/test/Core.Test/Context/CurrentContextTests.cs +++ b/test/Core.Test/Context/CurrentContextTests.cs @@ -107,6 +107,40 @@ public async Task BuildAsync_HttpContext_SetsDeviceType( Assert.Equal(deviceType, sutProvider.Sut.DeviceType); } + [Theory, BitAutoData] + public async Task BuildAsync_HttpContext_TestXConnectingIP( + SutProvider sutProvider) + { + var httpContext = new DefaultHttpContext(); + var globalSettings = new Core.Settings.GlobalSettings(); + // Arrange + globalSettings.SelfHosted = false; + httpContext.Request.Headers["X-Connecting-IP"] = "10.11.12.13"; + + // Act + await sutProvider.Sut.BuildAsync(httpContext, globalSettings); + + // Assert + Assert.Equal("10.11.12.13", sutProvider.Sut.IpAddress); + } + + [Theory, BitAutoData] + public async Task BuildAsync_HttpContext_TestXForwardedInSelfHosted( + SutProvider sutProvider) + { + var httpContext = new DefaultHttpContext(); + var globalSettings = new Core.Settings.GlobalSettings(); + // Arrange + globalSettings.SelfHosted = true; + httpContext.Request.Headers["X-Forwarded-For"] = "1.2.3.4,5.6.7.8"; + + // Act + await sutProvider.Sut.BuildAsync(httpContext, globalSettings); + + // Assert + Assert.Equal("1.2.3.4", sutProvider.Sut.IpAddress); + } + [Theory, BitAutoData] public async Task BuildAsync_HttpContext_SetsCloudflareFlags( SutProvider sutProvider)