diff --git a/AMWD.Common.AspNetCore/Extensions/HttpContextExtensions.cs b/AMWD.Common.AspNetCore/Extensions/HttpContextExtensions.cs index fee482c..2bcc8ab 100644 --- a/AMWD.Common.AspNetCore/Extensions/HttpContextExtensions.cs +++ b/AMWD.Common.AspNetCore/Extensions/HttpContextExtensions.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Net; using Microsoft.AspNetCore.Antiforgery; using Microsoft.Extensions.DependencyInjection; @@ -13,9 +14,9 @@ namespace Microsoft.AspNetCore.Http // Search these additional headers for a remote client ip address. private static readonly string[] defaultIpHeaderNames = new[] { - "X-Forwarded-For", // commonly used on all known proxies + "Cf-Connecting-Ip", // set by Cloudflare "X-Real-IP", // wide-spread alternative to X-Forwarded-For - "CF-Connecting-IP" // set by Cloudflare + "X-Forwarded-For", // commonly used on all known proxies }; /// @@ -37,9 +38,9 @@ namespace Microsoft.AspNetCore.Http /// /// Searches for additional headers in the following order: /// - /// X-Forwarded-For + /// Cf-Connecting-Ip /// X-Real-IP - /// CF-Connecting-IP + /// X-Forwarded-For /// /// /// The current . @@ -57,7 +58,11 @@ namespace Microsoft.AspNetCore.Http if (!httpContext.Request.Headers.ContainsKey(headerName)) continue; - forwardedForAddress = httpContext.Request.Headers[headerName].ToString(); + // X-Forwarded-For can contain multiple comma-separated addresses. + forwardedForAddress = httpContext.Request.Headers[headerName].ToString() + .Split(',', StringSplitOptions.TrimEntries) + .First(); + break; } @@ -73,9 +78,9 @@ namespace Microsoft.AspNetCore.Http /// /// Searches for additional headers in the following order: /// - /// X-Forwarded-For + /// Cf-Connecting-Ip /// X-Real-IP - /// CF-Connecting-IP + /// X-Forwarded-For /// /// /// The current . diff --git a/CHANGELOG.md b/CHANGELOG.md index 74c598c..afa302f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `ConfigureAwait(false)` to async calls where appropriate +### Changed + +- Changed order of header evaluation for remote IP address parsing: `Cf-Connecting-Ip`, `X-Real-IP`, `X-Forwarded-For` + +### Fixed + +- `GetRemoteIpAddress()` is able to get address when multiple proxies are used + ## test/v2.1.0 - 2023-10-13 diff --git a/UnitTests/AspNetCore/Extensions/HttpContextExtensionsTests.cs b/UnitTests/AspNetCore/Extensions/HttpContextExtensionsTests.cs index 9a6dc52..22ea737 100644 --- a/UnitTests/AspNetCore/Extensions/HttpContextExtensionsTests.cs +++ b/UnitTests/AspNetCore/Extensions/HttpContextExtensionsTests.cs @@ -113,9 +113,9 @@ namespace UnitTests.AspNetCore.Extensions } [DataTestMethod] - [DataRow("X-Forwarded-For")] + [DataRow("Cf-Connecting-Ip")] [DataRow("X-Real-IP")] - [DataRow("CF-Connecting-IP")] + [DataRow("X-Forwarded-For")] public void ShouldReturnDefaultHeader(string headerName) { // arrange @@ -170,6 +170,24 @@ namespace UnitTests.AspNetCore.Extensions Assert.AreEqual(remote, result); } + [TestMethod] + public void ShouldReturnFirstAddressOnMultipleProxies() + { + // arrange + remote = IPAddress.Parse("1.2.3.4"); + var header = IPAddress.Parse("5.6.7.8"); + requestHeaders.Add("X-Forwarded-For", $"{header}, 111.222.111.222"); + + var context = GetContext(); + + // act + var result = context.GetRemoteIpAddress(); + + // assert + Assert.AreNotEqual(remote, result); + Assert.AreEqual(header, result); + } + #endregion RemoteAddres #region Local Request