Skip to content

Commit 7e823a0

Browse files
author
Nate McMaster
committed
Merge release/2.2 from aspnet/BasicMiddleware
2 parents 1be2b42 + 44ec52e commit 7e823a0

31 files changed

+1050
-181
lines changed

src/Middleware/HostFiltering/sample/HostFilteringSample.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
4-
<TargetFrameworks>netcoreapp2.1;net461</TargetFrameworks>
4+
<TargetFrameworks>netcoreapp2.2;net461</TargetFrameworks>
55
</PropertyGroup>
66

77
<ItemGroup>

src/Middleware/HostFiltering/src/HostFilteringMiddleware.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,11 @@ private IList<StringSegment> EnsureConfigured()
102102
throw new InvalidOperationException("No allowed hosts were configured.");
103103
}
104104

105-
_logger.LogDebug("Allowed hosts: " + string.Join("; ", allowedHosts));
105+
if (_logger.IsEnabled(LogLevel.Debug))
106+
{
107+
_logger.LogDebug("Allowed hosts: {Hosts}", string.Join("; ", allowedHosts));
108+
}
109+
106110
_allowedHosts = allowedHosts;
107111
return _allowedHosts;
108112
}
@@ -148,29 +152,26 @@ private bool CheckHost(HttpContext context, IList<StringSegment> allowedHosts)
148152
// Http/1.1 requires the header but the value may be empty.
149153
if (!_options.AllowEmptyHosts)
150154
{
151-
_logger.LogInformation($"{context.Request.Protocol} request rejected due to missing or empty host header.");
155+
_logger.LogInformation("{Protocol} request rejected due to missing or empty host header.", context.Request.Protocol);
152156
return false;
153157
}
154-
if (_logger.IsEnabled(LogLevel.Debug))
155-
{
156-
_logger.LogDebug($"{context.Request.Protocol} request allowed with missing or empty host header.");
157-
}
158+
_logger.LogDebug("{Protocol} request allowed with missing or empty host header.", context.Request.Protocol);
158159
return true;
159160
}
160161

161162
if (_allowAnyNonEmptyHost == true)
162163
{
163-
_logger.LogTrace($"All hosts are allowed.");
164+
_logger.LogTrace("All hosts are allowed.");
164165
return true;
165166
}
166167

167168
if (HostString.MatchesAny(host, allowedHosts))
168169
{
169-
_logger.LogTrace($"The host '{host}' matches an allowed host.");
170+
_logger.LogTrace("The host '{Host}' matches an allowed host.", host);
170171
return true;
171172
}
172173

173-
_logger.LogInformation($"The host '{host}' does not match an allowed host.");
174+
_logger.LogInformation("The host '{Host}' does not match an allowed host.", host);
174175
return false;
175176
}
176177
}

src/Middleware/HttpOverrides/sample/HttpOverridesSample.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
4-
<TargetFrameworks>netcoreapp2.1;net461</TargetFrameworks>
4+
<TargetFrameworks>netcoreapp2.2;net461</TargetFrameworks>
55
</PropertyGroup>
66

77
<ItemGroup>

src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ public void ApplyForwarders(HttpContext context)
233233
if (currentValues.RemoteIpAndPort != null && checkKnownIps && !CheckKnownAddress(currentValues.RemoteIpAndPort.Address))
234234
{
235235
// Stop at the first unknown remote IP, but still apply changes processed so far.
236-
_logger.LogDebug(1, $"Unknown proxy: {currentValues.RemoteIpAndPort}");
236+
_logger.LogDebug(1, "Unknown proxy: {RemoteIpAndPort}", currentValues.RemoteIpAndPort);
237237
break;
238238
}
239239

@@ -248,12 +248,12 @@ public void ApplyForwarders(HttpContext context)
248248
else if (!string.IsNullOrEmpty(set.IpAndPortText))
249249
{
250250
// Stop at the first unparsable IP, but still apply changes processed so far.
251-
_logger.LogDebug(1, $"Unparsable IP: {set.IpAndPortText}");
251+
_logger.LogDebug(1, "Unparsable IP: {IpAndPortText}", set.IpAndPortText);
252252
break;
253253
}
254254
else if (_options.RequireHeaderSymmetry)
255255
{
256-
_logger.LogWarning(2, $"Missing forwarded IPAddress.");
256+
_logger.LogWarning(2, "Missing forwarded IPAddress.");
257257
return;
258258
}
259259
}
@@ -349,6 +349,14 @@ public void ApplyForwarders(HttpContext context)
349349

350350
private bool CheckKnownAddress(IPAddress address)
351351
{
352+
if (address.IsIPv4MappedToIPv6)
353+
{
354+
var ipv4Address = address.MapToIPv4();
355+
if (CheckKnownAddress(ipv4Address))
356+
{
357+
return true;
358+
}
359+
}
352360
if (_options.KnownProxies.Contains(address))
353361
{
354362
return true;

src/Middleware/HttpOverrides/src/Internal/IPEndPointParser.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System.Globalization;
45
using System.Net;
56

67
namespace Microsoft.AspNetCore.HttpOverrides.Internal
@@ -62,7 +63,7 @@ public static bool TryParse(string addressWithPort, out IPEndPoint endpoint)
6263
if (portPart != null)
6364
{
6465
int port;
65-
if (int.TryParse(portPart, out port))
66+
if (int.TryParse(portPart, NumberStyles.None, CultureInfo.InvariantCulture, out port))
6667
{
6768
endpoint = new IPEndPoint(address, port);
6869
return true;

src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,5 +822,48 @@ public async Task PartiallyEnabledForwardsPartiallyChangesRequest()
822822
Assert.Equal("localhost", context.Request.Host.ToString());
823823
Assert.Equal("Protocol", context.Request.Scheme);
824824
}
825+
826+
[Theory]
827+
[InlineData("22.33.44.55,::ffff:127.0.0.1", "", "", "22.33.44.55")]
828+
[InlineData("22.33.44.55,::ffff:172.123.142.121", "172.123.142.121", "", "22.33.44.55")]
829+
[InlineData("22.33.44.55,::ffff:172.123.142.121", "::ffff:172.123.142.121", "", "22.33.44.55")]
830+
[InlineData("22.33.44.55,::ffff:172.123.142.121,172.32.24.23", "", "172.0.0.0/8", "22.33.44.55")]
831+
[InlineData("2a00:1450:4009:802::200e,2a02:26f0:2d:183::356e,::ffff:172.123.142.121,172.32.24.23", "", "172.0.0.0/8,2a02:26f0:2d:183::1/64", "2a00:1450:4009:802::200e")]
832+
[InlineData("22.33.44.55,2a02:26f0:2d:183::356e,::ffff:127.0.0.1", "2a02:26f0:2d:183::356e", "", "22.33.44.55")]
833+
public async Task XForwardForIPv4ToIPv6Mapping(string forHeader, string knownProxies, string knownNetworks, string expectedRemoteIp)
834+
{
835+
var options = new ForwardedHeadersOptions
836+
{
837+
ForwardedHeaders = ForwardedHeaders.XForwardedFor,
838+
ForwardLimit = null,
839+
};
840+
841+
foreach (var knownProxy in knownProxies.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries))
842+
{
843+
var proxy = IPAddress.Parse(knownProxy);
844+
options.KnownProxies.Add(proxy);
845+
}
846+
foreach (var knownNetwork in knownNetworks.Split(new string[] { "," }, options:StringSplitOptions.RemoveEmptyEntries))
847+
{
848+
var knownNetworkParts = knownNetwork.Split('/');
849+
var networkIp = IPAddress.Parse(knownNetworkParts[0]);
850+
var prefixLength = int.Parse(knownNetworkParts[1]);
851+
options.KnownNetworks.Add(new IPNetwork(networkIp, prefixLength));
852+
}
853+
854+
var builder = new WebHostBuilder()
855+
.Configure(app =>
856+
{
857+
app.UseForwardedHeaders(options);
858+
});
859+
var server = new TestServer(builder);
860+
861+
var context = await server.SendAsync(c =>
862+
{
863+
c.Request.Headers["X-Forwarded-For"] = forHeader;
864+
});
865+
866+
Assert.Equal(expectedRemoteIp, context.Connection.RemoteIpAddress.ToString());
867+
}
825868
}
826869
}

src/Middleware/HttpsPolicy/sample/HttpsPolicySample.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
4-
<TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks>
4+
<TargetFrameworks>netcoreapp2.2;net461</TargetFrameworks>
55
</PropertyGroup>
66

77
<ItemGroup>

src/Middleware/HttpsPolicy/src/HstsMiddleware.cs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
using System.Globalization;
77
using System.Threading.Tasks;
88
using Microsoft.AspNetCore.Http;
9+
using Microsoft.AspNetCore.HttpsPolicy.Internal;
10+
using Microsoft.Extensions.Logging;
11+
using Microsoft.Extensions.Logging.Abstractions;
912
using Microsoft.Extensions.Options;
1013
using Microsoft.Extensions.Primitives;
1114
using Microsoft.Net.Http.Headers;
@@ -24,13 +27,15 @@ public class HstsMiddleware
2427
private readonly RequestDelegate _next;
2528
private readonly StringValues _strictTransportSecurityValue;
2629
private readonly IList<string> _excludedHosts;
30+
private readonly ILogger _logger;
2731

2832
/// <summary>
2933
/// Initialize the HSTS middleware.
3034
/// </summary>
3135
/// <param name="next"></param>
3236
/// <param name="options"></param>
33-
public HstsMiddleware(RequestDelegate next, IOptions<HstsOptions> options)
37+
/// <param name="loggerFactory"></param>
38+
public HstsMiddleware(RequestDelegate next, IOptions<HstsOptions> options, ILoggerFactory loggerFactory)
3439
{
3540
if (options == null)
3641
{
@@ -46,20 +51,39 @@ public HstsMiddleware(RequestDelegate next, IOptions<HstsOptions> options)
4651
var preload = hstsOptions.Preload ? Preload : StringSegment.Empty;
4752
_strictTransportSecurityValue = new StringValues($"max-age={maxAge}{includeSubdomains}{preload}");
4853
_excludedHosts = hstsOptions.ExcludedHosts;
54+
_logger = loggerFactory.CreateLogger<HstsMiddleware>();
4955
}
5056

57+
/// <summary>
58+
/// Initialize the HSTS middleware.
59+
/// </summary>
60+
/// <param name="next"></param>
61+
/// <param name="options"></param>
62+
public HstsMiddleware(RequestDelegate next, IOptions<HstsOptions> options)
63+
: this(next, options, NullLoggerFactory.Instance) { }
64+
5165
/// <summary>
5266
/// Invoke the middleware.
5367
/// </summary>
5468
/// <param name="context">The <see cref="HttpContext"/>.</param>
5569
/// <returns></returns>
5670
public Task Invoke(HttpContext context)
5771
{
58-
if (context.Request.IsHttps && !IsHostExcluded(context.Request.Host.Host))
72+
if (!context.Request.IsHttps)
73+
{
74+
_logger.SkippingInsecure();
75+
return _next(context);
76+
}
77+
78+
if (IsHostExcluded(context.Request.Host.Host))
5979
{
60-
context.Response.Headers[HeaderNames.StrictTransportSecurity] = _strictTransportSecurityValue;
80+
_logger.SkippingExcludedHost(context.Request.Host.Host);
81+
return _next(context);
6182
}
6283

84+
context.Response.Headers[HeaderNames.StrictTransportSecurity] = _strictTransportSecurityValue;
85+
_logger.AddingHstsHeader();
86+
6387
return _next(context);
6488
}
6589

src/Middleware/HttpsPolicy/src/HttpsRedirectionOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public class HttpsRedirectionOptions
2222
/// If the HttpsPort is not set, we will try to get the HttpsPort from the following:
2323
/// 1. HTTPS_PORT environment variable
2424
/// 2. IServerAddressesFeature
25-
/// 3. 443 (or not set)
25+
/// If that fails then the middleware will log a warning and turn off.
2626
/// </remarks>
2727
public int? HttpsPort { get; set; }
2828
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace Microsoft.AspNetCore.HttpsPolicy.Internal
8+
{
9+
internal static class HstsLoggingExtensions
10+
{
11+
private static readonly Action<ILogger, Exception> _notSecure;
12+
private static readonly Action<ILogger, string, Exception> _excludedHost;
13+
private static readonly Action<ILogger, Exception> _addingHstsHeader;
14+
15+
static HstsLoggingExtensions()
16+
{
17+
_notSecure = LoggerMessage.Define(LogLevel.Debug, 1, "The request is insecure. Skipping HSTS header.");
18+
_excludedHost = LoggerMessage.Define<string>(LogLevel.Debug, 2, "The host '{host}' is excluded. Skipping HSTS header.");
19+
_addingHstsHeader = LoggerMessage.Define(LogLevel.Trace, 3, "Adding HSTS header to response.");
20+
}
21+
22+
public static void SkippingInsecure(this ILogger logger)
23+
{
24+
_notSecure(logger, null);
25+
}
26+
27+
public static void SkippingExcludedHost(this ILogger logger, string host)
28+
{
29+
_excludedHost(logger, host, null);
30+
}
31+
32+
public static void AddingHstsHeader(this ILogger logger)
33+
{
34+
_addingHstsHeader(logger, null);
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)