Restrict x-forwarded-for evalutation. Credit: pmhsfelix.
Use x-forwarded-for validation to restrict other forwarders. Rename OverrideHeaderMiddleware to ForwardedHeadersMiddleware. Fix tests Fix package version.
This commit is contained in:
parent
ab34d08bc1
commit
44f03ef83f
|
|
@ -10,10 +10,9 @@ namespace HttpOverridesSample
|
|||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
app.UseIISPlatformHandler();
|
||||
app.UseOverrideHeaders(new OverrideHeaderOptions
|
||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedOptions = ForwardedHeaders.All
|
||||
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
|
||||
});
|
||||
app.UseHttpMethodOverride();
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
"emitEntryPoint": true
|
||||
},
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.HttpOverrides": "1.0.0-*",
|
||||
"Microsoft.AspNetCore.IISPlatformHandler": "1.0.0-*",
|
||||
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0-*",
|
||||
"Microsoft.AspNetCore.HttpOverrides": "0.1.0-*"
|
||||
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0-*"
|
||||
},
|
||||
"commands": {
|
||||
"web": "HttpOverridesSample"
|
||||
|
|
@ -23,4 +23,4 @@
|
|||
"**.user",
|
||||
"**.vspscc"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
|
@ -7,31 +7,30 @@ using Microsoft.Extensions.Options;
|
|||
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
public static class OverrideHeaderExtensions
|
||||
public static class ForwardedHeadersExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Forwards proxied headers onto current request
|
||||
/// </summary>
|
||||
/// <param name="builder"></param>
|
||||
/// <param name="options">Enables the different override options.</param>
|
||||
/// <returns></returns>
|
||||
public static IApplicationBuilder UseOverrideHeaders(this IApplicationBuilder builder)
|
||||
public static IApplicationBuilder UseForwardedHeaders(this IApplicationBuilder builder)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
return builder.UseMiddleware<OverrideHeaderMiddleware>();
|
||||
return builder.UseMiddleware<ForwardedHeadersMiddleware>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forwards proxied headers onto current request
|
||||
/// </summary>
|
||||
/// <param name="builder"></param>
|
||||
/// <param name="options">Enables the different override options.</param>
|
||||
/// <param name="options">Enables the different forwarding options.</param>
|
||||
/// <returns></returns>
|
||||
public static IApplicationBuilder UseOverrideHeaders(this IApplicationBuilder builder, OverrideHeaderOptions options)
|
||||
public static IApplicationBuilder UseForwardedHeaders(this IApplicationBuilder builder, ForwardedHeadersOptions options)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
|
|
@ -42,7 +41,7 @@ namespace Microsoft.AspNetCore.Builder
|
|||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
return builder.UseMiddleware<OverrideHeaderMiddleware>(Options.Create(options));
|
||||
return builder.UseMiddleware<ForwardedHeadersMiddleware>(Options.Create(options));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,275 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.HttpOverrides.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.AspNetCore.HttpOverrides
|
||||
{
|
||||
public class ForwardedHeadersMiddleware
|
||||
{
|
||||
private const string XForwardedForHeaderName = "X-Forwarded-For";
|
||||
private const string XForwardedHostHeaderName = "X-Forwarded-Host";
|
||||
private const string XForwardedProtoHeaderName = "X-Forwarded-Proto";
|
||||
private const string XOriginalForName = "X-Original-For";
|
||||
private const string XOriginalHostName = "X-Original-Host";
|
||||
private const string XOriginalProtoName = "X-Original-Proto";
|
||||
|
||||
private readonly ForwardedHeadersOptions _options;
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public ForwardedHeadersMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, IOptions<ForwardedHeadersOptions> options)
|
||||
{
|
||||
if (next == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(next));
|
||||
}
|
||||
if (loggerFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loggerFactory));
|
||||
}
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
_options = options.Value;
|
||||
_logger = loggerFactory.CreateLogger<ForwardedHeadersMiddleware>();
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public Task Invoke(HttpContext context)
|
||||
{
|
||||
ApplyForwarders(context);
|
||||
return _next(context);
|
||||
}
|
||||
|
||||
public void ApplyForwarders(HttpContext context)
|
||||
{
|
||||
// Gather expected headers. Enabled headers must have the same number of entries.
|
||||
string[] forwardedFor = null, forwardedProto = null, forwardedHost = null;
|
||||
bool checkFor = false, checkProto = false, checkHost = false;
|
||||
int entryCount = 0;
|
||||
|
||||
if ((_options.ForwardedHeaders & ForwardedHeaders.XForwardedFor) == ForwardedHeaders.XForwardedFor)
|
||||
{
|
||||
checkFor = true;
|
||||
forwardedFor = context.Request.Headers.GetCommaSeparatedValues(XForwardedForHeaderName);
|
||||
if (StringValues.IsNullOrEmpty(forwardedFor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
entryCount = forwardedFor.Length;
|
||||
}
|
||||
|
||||
if ((_options.ForwardedHeaders & ForwardedHeaders.XForwardedProto) == ForwardedHeaders.XForwardedProto)
|
||||
{
|
||||
checkProto = true;
|
||||
forwardedProto = context.Request.Headers.GetCommaSeparatedValues(XForwardedProtoHeaderName);
|
||||
if (StringValues.IsNullOrEmpty(forwardedProto))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (checkFor && forwardedFor.Length != forwardedProto.Length)
|
||||
{
|
||||
_logger.LogDebug(1, "Parameter count mismatch between X-Forwarded-For and X-Forwarded-Proto.");
|
||||
return;
|
||||
}
|
||||
entryCount = forwardedProto.Length;
|
||||
}
|
||||
|
||||
if ((_options.ForwardedHeaders & ForwardedHeaders.XForwardedHost) == ForwardedHeaders.XForwardedHost)
|
||||
{
|
||||
checkHost = true;
|
||||
forwardedHost = context.Request.Headers.GetCommaSeparatedValues(XForwardedHostHeaderName);
|
||||
if (StringValues.IsNullOrEmpty(forwardedHost))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ((checkFor && forwardedFor.Length != forwardedHost.Length)
|
||||
|| (checkProto && forwardedProto.Length != forwardedHost.Length))
|
||||
{
|
||||
_logger.LogDebug(1, "Parameter count mismatch between X-Forwarded-Host and X-Forwarded-For or X-Forwarded-Proto.");
|
||||
return;
|
||||
}
|
||||
entryCount = forwardedHost.Length;
|
||||
}
|
||||
|
||||
// Apply ForwardLimit, if any
|
||||
int offset = 0;
|
||||
if (_options.ForwardLimit.HasValue && entryCount > _options.ForwardLimit)
|
||||
{
|
||||
offset = entryCount - _options.ForwardLimit.Value;
|
||||
entryCount = _options.ForwardLimit.Value;
|
||||
}
|
||||
|
||||
// Group the data together.
|
||||
var sets = new List<SetOfForwarders>(entryCount);
|
||||
for (int i = 0; i < entryCount; i++)
|
||||
{
|
||||
var set = new SetOfForwarders();
|
||||
if (checkFor)
|
||||
{
|
||||
set.IpAndPortText = forwardedFor[offset + i];
|
||||
}
|
||||
if (checkProto)
|
||||
{
|
||||
set.Scheme = forwardedProto[offset + i];
|
||||
}
|
||||
if (checkHost)
|
||||
{
|
||||
set.Host = forwardedHost[offset + i];
|
||||
}
|
||||
sets.Add(set);
|
||||
}
|
||||
// They get processed in reverse order, right to left.
|
||||
sets.Reverse();
|
||||
|
||||
// Gather initial values
|
||||
var connection = context.Connection;
|
||||
var request = context.Request;
|
||||
var currentValues = new SetOfForwarders()
|
||||
{
|
||||
RemoteIpAndPort = connection.RemoteIpAddress != null ? new IPEndPoint(connection.RemoteIpAddress, connection.RemotePort) : null,
|
||||
// Host and Scheme initial values are never inspected, no need to set them here.
|
||||
};
|
||||
|
||||
var checkKnownIps = _options.KnownNetworks.Count > 0 || _options.KnownProxies.Count > 0;
|
||||
bool applyChanges = false;
|
||||
int entriesConsumed = 0;
|
||||
|
||||
foreach (var set in sets)
|
||||
{
|
||||
if (checkFor)
|
||||
{
|
||||
// For the first instance, allow remoteIp to be null for servers that don't support it natively.
|
||||
if (currentValues.RemoteIpAndPort != null && checkKnownIps && !CheckKnownAddress(currentValues.RemoteIpAndPort.Address))
|
||||
{
|
||||
// Stop at the first unknown remote IP, but still apply changes processed so far.
|
||||
_logger.LogDebug(1, $"Unknown proxy: {currentValues.RemoteIpAndPort}");
|
||||
break;
|
||||
}
|
||||
if (!IPEndPointParser.TryParse(set.IpAndPortText, out set.RemoteIpAndPort))
|
||||
{
|
||||
_logger.LogDebug(2, $"Failed to parse forwarded IPAddress: {currentValues.IpAndPortText}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (checkProto)
|
||||
{
|
||||
if (string.IsNullOrEmpty(set.Scheme))
|
||||
{
|
||||
_logger.LogDebug(3, $"Failed to parse forwarded scheme: {set.Scheme}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (checkHost)
|
||||
{
|
||||
if (string.IsNullOrEmpty(set.Host))
|
||||
{
|
||||
_logger.LogDebug(4, $"Failed to parse forwarded host: {set.Host}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
applyChanges = true;
|
||||
currentValues = set;
|
||||
entriesConsumed++;
|
||||
}
|
||||
|
||||
if (applyChanges)
|
||||
{
|
||||
if (checkFor)
|
||||
{
|
||||
if (connection.RemoteIpAddress != null)
|
||||
{
|
||||
// Save the original
|
||||
request.Headers[XOriginalForName] = new IPEndPoint(connection.RemoteIpAddress, connection.RemotePort).ToString();
|
||||
}
|
||||
if (forwardedFor.Length > entriesConsumed)
|
||||
{
|
||||
// Truncate the consumed header values
|
||||
request.Headers[XForwardedForHeaderName] = forwardedFor.Take(forwardedFor.Length - entriesConsumed).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
// All values were consumed
|
||||
request.Headers.Remove(XForwardedForHeaderName);
|
||||
}
|
||||
connection.RemoteIpAddress = currentValues.RemoteIpAndPort.Address;
|
||||
connection.RemotePort = currentValues.RemoteIpAndPort.Port;
|
||||
}
|
||||
|
||||
if (checkProto)
|
||||
{
|
||||
// Save the original
|
||||
request.Headers[XOriginalProtoName] = request.Scheme;
|
||||
if (forwardedProto.Length > entriesConsumed)
|
||||
{
|
||||
// Truncate the consumed header values
|
||||
request.Headers[XForwardedProtoHeaderName] = forwardedProto.Take(forwardedProto.Length - entriesConsumed).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
// All values were consumed
|
||||
request.Headers.Remove(XForwardedProtoHeaderName);
|
||||
}
|
||||
request.Scheme = currentValues.Scheme;
|
||||
}
|
||||
|
||||
if (checkHost)
|
||||
{
|
||||
// Save the original
|
||||
request.Headers[XOriginalHostName] = request.Host.ToString();
|
||||
if (forwardedHost.Length > entriesConsumed)
|
||||
{
|
||||
// Truncate the consumed header values
|
||||
request.Headers[XForwardedHostHeaderName] = forwardedHost.Take(forwardedHost.Length - entriesConsumed).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
// All values were consumed
|
||||
request.Headers.Remove(XForwardedHostHeaderName);
|
||||
}
|
||||
request.Host = HostString.FromUriComponent(currentValues.Host);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckKnownAddress(IPAddress address)
|
||||
{
|
||||
if (_options.KnownProxies.Contains(address))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
foreach (var network in _options.KnownNetworks)
|
||||
{
|
||||
if (network.Contains(address))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private class SetOfForwarders
|
||||
{
|
||||
public string IpAndPortText;
|
||||
public IPEndPoint RemoteIpAndPort;
|
||||
public string Host;
|
||||
public string Scheme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
public class ForwardedHeadersOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifies which forwarders should be processed.
|
||||
/// </summary>
|
||||
public ForwardedHeaders ForwardedHeaders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Limits the number of entries in the headers that will be processed. The default value is 1.
|
||||
/// Set to null to disable the limit, but this should only be done if
|
||||
/// KnownProxies or KnownNetworks are configured.
|
||||
/// </summary>
|
||||
public int? ForwardLimit { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Addresses of known proxies to accept forwarded headers from.
|
||||
/// </summary>
|
||||
public IList<IPAddress> KnownProxies { get; } = new List<IPAddress>() { IPAddress.IPv6Loopback };
|
||||
|
||||
/// <summary>
|
||||
/// Address ranges of known proxies to accept forwarded headers from.
|
||||
/// </summary>
|
||||
public IList<IPNetwork> KnownNetworks { get; } = new List<IPNetwork>() { new IPNetwork(IPAddress.Loopback, 8) };
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Net;
|
||||
|
||||
namespace Microsoft.AspNetCore.HttpOverrides
|
||||
{
|
||||
public class IPNetwork
|
||||
{
|
||||
public IPNetwork(IPAddress prefix, int prefixLength)
|
||||
{
|
||||
Prefix = prefix;
|
||||
PrefixLength = prefixLength;
|
||||
PrefixBytes = Prefix.GetAddressBytes();
|
||||
Mask = CreateMask();
|
||||
}
|
||||
|
||||
public IPAddress Prefix { get; }
|
||||
|
||||
private byte[] PrefixBytes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The CIDR notation of the subnet mask
|
||||
/// </summary>
|
||||
public int PrefixLength { get; }
|
||||
|
||||
private byte[] Mask { get; }
|
||||
|
||||
public bool Contains(IPAddress address)
|
||||
{
|
||||
if (Prefix.AddressFamily != address.AddressFamily)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var addressBytes = address.GetAddressBytes();
|
||||
for (int i = 0; i < PrefixBytes.Length && Mask[i] != 0; i++)
|
||||
{
|
||||
if (PrefixBytes[i] != (addressBytes[i] & Mask[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private byte[] CreateMask()
|
||||
{
|
||||
var mask = new byte[PrefixBytes.Length];
|
||||
int remainingBits = PrefixLength;
|
||||
int i = 0;
|
||||
while (remainingBits >= 8)
|
||||
{
|
||||
mask[i] = 0xFF;
|
||||
i++;
|
||||
remainingBits -= 8;
|
||||
}
|
||||
if (remainingBits > 0)
|
||||
{
|
||||
mask[i] = (byte)(0xFF << (8 - remainingBits));
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
using System.Net;
|
||||
|
||||
namespace Microsoft.AspNetCore.HttpOverrides
|
||||
namespace Microsoft.AspNetCore.HttpOverrides.Internal
|
||||
{
|
||||
public static class IPEndPointParser
|
||||
{
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.HttpOverrides
|
||||
{
|
||||
public class OverrideHeaderMiddleware
|
||||
{
|
||||
private const string XForwardedForHeaderName = "X-Forwarded-For";
|
||||
private const string XForwardedHostHeaderName = "X-Forwarded-Host";
|
||||
private const string XForwardedProtoHeaderName = "X-Forwarded-Proto";
|
||||
private const string XOriginalForName = "X-Original-For";
|
||||
private const string XOriginalHostName = "X-Original-Host";
|
||||
private const string XOriginalProtoName = "X-Original-Proto";
|
||||
private readonly OverrideHeaderOptions _options;
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
public OverrideHeaderMiddleware(RequestDelegate next, IOptions<OverrideHeaderOptions> options)
|
||||
{
|
||||
if (next == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(next));
|
||||
}
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
_options = options.Value;
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public Task Invoke(HttpContext context)
|
||||
{
|
||||
UpdateRemoteIp(context);
|
||||
|
||||
UpdateHost(context);
|
||||
|
||||
UpdateScheme(context);
|
||||
|
||||
return _next(context);
|
||||
}
|
||||
|
||||
private void UpdateRemoteIp(HttpContext context)
|
||||
{
|
||||
if ((_options.ForwardedOptions & ForwardedHeaders.XForwardedFor) != 0)
|
||||
{
|
||||
var xForwardedForHeaderValue = context.Request.Headers.GetCommaSeparatedValues(XForwardedForHeaderName);
|
||||
if (xForwardedForHeaderValue != null && xForwardedForHeaderValue.Length > 0)
|
||||
{
|
||||
IPEndPoint endpoint;
|
||||
if (IPEndPointParser.TryParse(xForwardedForHeaderValue[0], out endpoint))
|
||||
{
|
||||
var connection = context.Connection;
|
||||
var remoteIP = connection.RemoteIpAddress;
|
||||
if (remoteIP != null)
|
||||
{
|
||||
var remoteIPString = new IPEndPoint(remoteIP, connection.RemotePort).ToString();
|
||||
context.Request.Headers[XOriginalForName] = remoteIPString;
|
||||
}
|
||||
connection.RemoteIpAddress = endpoint.Address;
|
||||
connection.RemotePort = endpoint.Port;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateHost(HttpContext context)
|
||||
{
|
||||
if ((_options.ForwardedOptions & ForwardedHeaders.XForwardedHost) != 0)
|
||||
{
|
||||
var xForwardHostHeaderValue = context.Request.Headers[XForwardedHostHeaderName];
|
||||
if (!string.IsNullOrEmpty(xForwardHostHeaderValue))
|
||||
{
|
||||
var hostString = context.Request.Host.ToString();
|
||||
if (!string.IsNullOrEmpty(hostString))
|
||||
{
|
||||
context.Request.Headers[XOriginalHostName] = hostString;
|
||||
}
|
||||
context.Request.Host = HostString.FromUriComponent(xForwardHostHeaderValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateScheme(HttpContext context)
|
||||
{
|
||||
if ((_options.ForwardedOptions & ForwardedHeaders.XForwardedProto) != 0)
|
||||
{
|
||||
var xForwardProtoHeaderValue = context.Request.Headers[XForwardedProtoHeaderName];
|
||||
if (!string.IsNullOrEmpty(xForwardProtoHeaderValue))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(context.Request.Scheme))
|
||||
{
|
||||
context.Request.Headers[XOriginalProtoName] = context.Request.Scheme;
|
||||
}
|
||||
context.Request.Scheme = xForwardProtoHeaderValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
public class OverrideHeaderOptions
|
||||
{
|
||||
public ForwardedHeaders ForwardedOptions { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "0.1.0-*",
|
||||
"version": "1.0.0-*",
|
||||
"compilationOptions": {
|
||||
"warningsAsErrors": true,
|
||||
"keyFile": "../../tools/Key.snk"
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.Http.Extensions": "1.0.0-*",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "1.0.0-*",
|
||||
"Microsoft.Extensions.Options": "1.0.0-*"
|
||||
},
|
||||
"frameworks": {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,522 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.HttpOverrides
|
||||
{
|
||||
public class ForwardedHeadersMiddlewareTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task XForwardedForDefaultSettingsChangeRemoteIpAndPort()
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.XForwardedFor
|
||||
});
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Equal("11.111.111.11", context.Connection.RemoteIpAddress.ToString());
|
||||
Assert.Equal(9090, context.Connection.RemotePort);
|
||||
// No Original set if RemoteIpAddress started null.
|
||||
Assert.False(context.Request.Headers.ContainsKey("X-Original-For"));
|
||||
// Should have been consumed and removed
|
||||
Assert.False(context.Request.Headers.ContainsKey("X-Forwarded-For"));
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.Add("X-Forwarded-For", "11.111.111.11:9090");
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1, "11.111.111.11.12345", "10.0.0.1", 99)] // Invalid
|
||||
public async Task XForwardedForFirstValueIsInvalid(int limit, string header, string expectedIp, int expectedPort)
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.Use((context, next) =>
|
||||
{
|
||||
context.Connection.RemoteIpAddress = IPAddress.Parse("10.0.0.1");
|
||||
context.Connection.RemotePort = 99;
|
||||
return next();
|
||||
});
|
||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.XForwardedFor,
|
||||
ForwardLimit = limit,
|
||||
});
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Equal(expectedIp, context.Connection.RemoteIpAddress.ToString());
|
||||
Assert.Equal(expectedPort, context.Connection.RemotePort);
|
||||
Assert.False(context.Request.Headers.ContainsKey("X-Original-For"));
|
||||
Assert.True(context.Request.Headers.ContainsKey("X-Forwarded-For"));
|
||||
Assert.Equal(header, context.Request.Headers["X-Forwarded-For"]);
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.TryAddWithoutValidation("X-Forwarded-For", header);
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1, "11.111.111.11:12345", "11.111.111.11", 12345, "")]
|
||||
[InlineData(10, "11.111.111.11:12345", "11.111.111.11", 12345, "")]
|
||||
[InlineData(1, "12.112.112.12:23456, 11.111.111.11:12345", "11.111.111.11", 12345, "12.112.112.12:23456")]
|
||||
[InlineData(2, "12.112.112.12:23456, 11.111.111.11:12345", "12.112.112.12", 23456, "")]
|
||||
[InlineData(10, "12.112.112.12:23456, 11.111.111.11:12345", "12.112.112.12", 23456, "")]
|
||||
[InlineData(10, "12.112.112.12.23456, 11.111.111.11:12345", "10.0.0.1", 99, "12.112.112.12.23456, 11.111.111.11:12345")] // Invalid
|
||||
[InlineData(10, "13.113.113.13:34567, 12.112.112.12.23456, 11.111.111.11:12345", "10.0.0.1", 99,
|
||||
"13.113.113.13:34567, 12.112.112.12.23456, 11.111.111.11:12345")] // Invalid
|
||||
[InlineData(2, "13.113.113.13:34567, 12.112.112.12:23456, 11.111.111.11:12345", "12.112.112.12", 23456, "13.113.113.13:34567")]
|
||||
[InlineData(3, "13.113.113.13:34567, 12.112.112.12:23456, 11.111.111.11:12345", "13.113.113.13", 34567, "")]
|
||||
public async Task XForwardedForForwardLimit(int limit, string header, string expectedIp, int expectedPort, string remainingHeader)
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.Use((context, next) =>
|
||||
{
|
||||
context.Connection.RemoteIpAddress = IPAddress.Parse("10.0.0.1");
|
||||
context.Connection.RemotePort = 99;
|
||||
return next();
|
||||
});
|
||||
var options = new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.XForwardedFor,
|
||||
ForwardLimit = limit,
|
||||
};
|
||||
options.KnownProxies.Clear();
|
||||
options.KnownNetworks.Clear();
|
||||
app.UseForwardedHeaders(options);
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Equal(expectedIp, context.Connection.RemoteIpAddress.ToString());
|
||||
Assert.Equal(expectedPort, context.Connection.RemotePort);
|
||||
Assert.Equal(remainingHeader, context.Request.Headers["X-Forwarded-For"].ToString());
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.TryAddWithoutValidation("X-Forwarded-For", header);
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("11.111.111.11", false)]
|
||||
[InlineData("127.0.0.1", true)]
|
||||
[InlineData("127.0.1.1", true)]
|
||||
[InlineData("::1", true)]
|
||||
[InlineData("::", false)]
|
||||
public async Task XForwardedForLoopback(string originalIp, bool expectForwarded)
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.Use((context, next) =>
|
||||
{
|
||||
context.Connection.RemoteIpAddress = IPAddress.Parse(originalIp);
|
||||
context.Connection.RemotePort = 99;
|
||||
return next();
|
||||
});
|
||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.XForwardedFor,
|
||||
});
|
||||
app.Run(context =>
|
||||
{
|
||||
if (expectForwarded)
|
||||
{
|
||||
Assert.Equal("10.0.0.1", context.Connection.RemoteIpAddress.ToString());
|
||||
Assert.Equal(1234, context.Connection.RemotePort);
|
||||
Assert.True(context.Request.Headers.ContainsKey("X-Original-For"));
|
||||
Assert.Equal(new IPEndPoint(IPAddress.Parse(originalIp), 99).ToString(),
|
||||
context.Request.Headers["X-Original-For"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal(originalIp, context.Connection.RemoteIpAddress.ToString());
|
||||
Assert.Equal(99, context.Connection.RemotePort);
|
||||
Assert.False(context.Request.Headers.ContainsKey("X-Original-For"));
|
||||
}
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.TryAddWithoutValidation("X-Forwarded-For", "10.0.0.1:1234");
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1, "11.111.111.11:12345", "20.0.0.1", "10.0.0.1", 99)]
|
||||
[InlineData(1, "", "10.0.0.1", "10.0.0.1", 99)]
|
||||
[InlineData(1, "11.111.111.11:12345", "10.0.0.1", "11.111.111.11", 12345)]
|
||||
[InlineData(1, "12.112.112.12:23456, 11.111.111.11:12345", "10.0.0.1", "11.111.111.11", 12345)]
|
||||
[InlineData(2, "12.112.112.12:23456, 11.111.111.11:12345", "10.0.0.1", "11.111.111.11", 12345)]
|
||||
[InlineData(1, "12.112.112.12:23456, 11.111.111.11:12345", "10.0.0.1,11.111.111.11", "11.111.111.11", 12345)]
|
||||
[InlineData(2, "12.112.112.12:23456, 11.111.111.11:12345", "10.0.0.1,11.111.111.11", "12.112.112.12", 23456)]
|
||||
[InlineData(1, "12.112.112.12:23456, 11.111.111.11:12345", "10.0.0.1,11.111.111.11,12.112.112.12", "11.111.111.11", 12345)]
|
||||
[InlineData(2, "12.112.112.12:23456, 11.111.111.11:12345", "10.0.0.1,11.111.111.11,12.112.112.12", "12.112.112.12", 23456)]
|
||||
[InlineData(3, "13.113.113.13:34567, 12.112.112.12:23456, 11.111.111.11:12345", "10.0.0.1,11.111.111.11,12.112.112.12", "13.113.113.13", 34567)]
|
||||
[InlineData(3, "13.113.113.13:34567, 12.112.112.12;23456, 11.111.111.11:12345", "10.0.0.1,11.111.111.11,12.112.112.12", "10.0.0.1", 99)] // Invalid 2nd IP
|
||||
[InlineData(3, "13.113.113.13;34567, 12.112.112.12:23456, 11.111.111.11:12345", "10.0.0.1,11.111.111.11,12.112.112.12", "10.0.0.1", 99)] // Invalid 3rd IP
|
||||
public async Task XForwardedForForwardKnownIps(int limit, string header, string knownIPs, string expectedIp, int expectedPort)
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.Use((context, next) =>
|
||||
{
|
||||
context.Connection.RemoteIpAddress = IPAddress.Parse("10.0.0.1");
|
||||
context.Connection.RemotePort = 99;
|
||||
return next();
|
||||
});
|
||||
var options = new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.XForwardedFor,
|
||||
ForwardLimit = limit,
|
||||
};
|
||||
foreach (var ip in knownIPs.Split(',').Select(text => IPAddress.Parse(text)))
|
||||
{
|
||||
options.KnownProxies.Add(ip);
|
||||
}
|
||||
app.UseForwardedHeaders(options);
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Equal(expectedIp, context.Connection.RemoteIpAddress.ToString());
|
||||
Assert.Equal(expectedPort, context.Connection.RemotePort);
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.TryAddWithoutValidation("X-Forwarded-For", header);
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task XForwardedForOverrideBadIpDoesntChangeRemoteIp()
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.XForwardedFor
|
||||
});
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Null(context.Connection.RemoteIpAddress);
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.Add("X-Forwarded-For", "BAD-IP");
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task XForwardedHostOverrideChangesRequestHost()
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.XForwardedHost
|
||||
});
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Equal("testhost", context.Request.Host.ToString());
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.Add("X-Forwarded-Host", "testhost");
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, "h1", "http")]
|
||||
[InlineData(1, "", "http")]
|
||||
[InlineData(1, "h1", "h1")]
|
||||
[InlineData(3, "h1", "h1")]
|
||||
[InlineData(1, "h2, h1", "h1")]
|
||||
[InlineData(2, "h2, h1", "h2")]
|
||||
[InlineData(10, "h3, h2, h1", "h3")]
|
||||
public async Task XForwardedProtoOverrideChangesRequestProtocol(int limit, string header, string expected)
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.XForwardedProto,
|
||||
ForwardLimit = limit,
|
||||
});
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Equal(expected, context.Request.Scheme);
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.Add("X-Forwarded-Proto", header);
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, "h1", "::1", "http")]
|
||||
[InlineData(1, "", "::1", "http")]
|
||||
[InlineData(1, "h1", "::1", "h1")]
|
||||
[InlineData(3, "h1", "::1", "h1")]
|
||||
[InlineData(3, "h2, h1", "::1", "http")]
|
||||
[InlineData(5, "h2, h1", "::1, ::1", "h2")]
|
||||
[InlineData(10, "h3, h2, h1", "::1, ::1, ::1", "h3")]
|
||||
[InlineData(10, "h3, h2, h1", "::1, badip, ::1", "http")]
|
||||
public async Task XForwardedProtoOverrideLimitedByXForwardedForCount(int limit, string protoHeader, string forHeader, string expected)
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedFor,
|
||||
ForwardLimit = limit,
|
||||
});
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Equal(expected, context.Request.Scheme);
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.Add("X-Forwarded-Proto", protoHeader);
|
||||
req.Headers.Add("X-Forwarded-For", forHeader);
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", "", "::1", false, "http")]
|
||||
[InlineData("h1", "", "::1", false, "http")]
|
||||
[InlineData("h1", "F::", "::1", false, "h1")]
|
||||
[InlineData("h1", "F::", "E::", false, "h1")]
|
||||
[InlineData("", "", "::1", true, "http")]
|
||||
[InlineData("h1", "", "::1", true, "http")]
|
||||
[InlineData("h1", "F::", "::1", true, "h1")]
|
||||
[InlineData("h1", "", "F::", true, "http")]
|
||||
[InlineData("h1", "E::", "F::", true, "http")]
|
||||
[InlineData("h2, h1", "", "::1", true, "http")]
|
||||
[InlineData("h2, h1", "F::, D::", "::1", true, "h1")]
|
||||
[InlineData("h2, h1", "E::, D::", "F::", true, "http")]
|
||||
[InlineData("h2, h1", "E::, D::", "F::", true, "http")]
|
||||
public async Task XForwardedProtoOverrideLimitedByLoopback(string protoHeader, string forHeader, string remoteIp, bool loopback, string expected)
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.Use((context, next) =>
|
||||
{
|
||||
context.Connection.RemoteIpAddress = IPAddress.Parse(remoteIp);
|
||||
return next();
|
||||
});
|
||||
var options = new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedFor,
|
||||
ForwardLimit = 5,
|
||||
};
|
||||
if (!loopback)
|
||||
{
|
||||
options.KnownNetworks.Clear();
|
||||
options.KnownProxies.Clear();
|
||||
}
|
||||
app.UseForwardedHeaders(options);
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Equal(expected, context.Request.Scheme);
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.Add("X-Forwarded-Proto", protoHeader);
|
||||
req.Headers.Add("X-Forwarded-For", forHeader);
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AllForwardsDisabledByDefault()
|
||||
{
|
||||
var options = new ForwardedHeadersOptions();
|
||||
Assert.True(options.ForwardedHeaders == ForwardedHeaders.None);
|
||||
Assert.Equal(1, options.ForwardLimit);
|
||||
Assert.Equal(1, options.KnownNetworks.Count());
|
||||
Assert.Equal(1, options.KnownProxies.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AllForwardsEnabledChangeRequestRemoteIpHostandProtocol()
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.All
|
||||
});
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Equal("11.111.111.11", context.Connection.RemoteIpAddress.ToString());
|
||||
Assert.Equal("testhost", context.Request.Host.ToString());
|
||||
Assert.Equal("Protocol", context.Request.Scheme);
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.Add("X-Forwarded-For", "11.111.111.11");
|
||||
req.Headers.Add("X-Forwarded-Host", "testhost");
|
||||
req.Headers.Add("X-Forwarded-Proto", "Protocol");
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AllOptionsDisabledRequestDoesntChange()
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.None
|
||||
});
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Null(context.Connection.RemoteIpAddress);
|
||||
Assert.Equal("localhost", context.Request.Host.ToString());
|
||||
Assert.Equal("http", context.Request.Scheme);
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.Add("X-Forwarded-For", "11.111.111.11");
|
||||
req.Headers.Add("X-Forwarded-Host", "otherhost");
|
||||
req.Headers.Add("X-Forwarded-Proto", "Protocol");
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PartiallyEnabledForwardsPartiallyChangesRequest()
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
|
||||
});
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Equal("11.111.111.11", context.Connection.RemoteIpAddress.ToString());
|
||||
Assert.Equal("localhost", context.Request.Host.ToString());
|
||||
Assert.Equal("Protocol", context.Request.Scheme);
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.Add("X-Forwarded-For", "11.111.111.11");
|
||||
req.Headers.Add("X-Forwarded-Proto", "Protocol");
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
using System.Net;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.HttpOverrides
|
||||
namespace Microsoft.AspNetCore.HttpOverrides.Internal
|
||||
{
|
||||
public class IPEndPointParserTests
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Net;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.HttpOverrides
|
||||
{
|
||||
public class IPNetworkTest
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("10.1.1.0", 8, "10.1.1.10")]
|
||||
[InlineData("174.0.0.0", 7, "175.1.1.10")]
|
||||
[InlineData("10.174.0.0", 15, "10.175.1.10")]
|
||||
[InlineData("10.168.0.0", 14, "10.171.1.10")]
|
||||
public void Contains_Positive(string prefixText, int length, string addressText)
|
||||
{
|
||||
var network = new IPNetwork(IPAddress.Parse(prefixText), length);
|
||||
Assert.True(network.Contains(IPAddress.Parse(addressText)));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("10.1.0.0", 16, "10.2.1.10")]
|
||||
[InlineData("174.0.0.0", 7, "173.1.1.10")]
|
||||
[InlineData("10.174.0.0", 15, "10.173.1.10")]
|
||||
[InlineData("10.168.0.0", 14, "10.172.1.10")]
|
||||
public void Contains_Negative(string prefixText, int length, string addressText)
|
||||
{
|
||||
var network = new IPNetwork(IPAddress.Parse(prefixText), length);
|
||||
Assert.False(network.Contains(IPAddress.Parse(addressText)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,223 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.HttpOverrides
|
||||
{
|
||||
public class OverrideMiddlewareHeaderTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task XForwardedForOverrideChangesRemoteIp()
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseOverrideHeaders(new OverrideHeaderOptions
|
||||
{
|
||||
ForwardedOptions = ForwardedHeaders.XForwardedFor
|
||||
});
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Equal("11.111.111.11", context.Connection.RemoteIpAddress.ToString());
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.Add("X-Forwarded-For", "11.111.111.11");
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task XForwardedForOverrideBadIpDoesntChangeRemoteIp()
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseOverrideHeaders(new OverrideHeaderOptions
|
||||
{
|
||||
ForwardedOptions = ForwardedHeaders.XForwardedFor
|
||||
});
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Null(context.Connection.RemoteIpAddress);
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.Add("X-Forwarded-For", "BAD-IP");
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task XForwardedHostOverrideChangesRequestHost()
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseOverrideHeaders(new OverrideHeaderOptions
|
||||
{
|
||||
ForwardedOptions = ForwardedHeaders.XForwardedHost
|
||||
});
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Equal("testhost", context.Request.Host.ToString());
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.Add("X-Forwarded-Host", "testhost");
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task XForwardedProtoOverrideChangesRequestProtocol()
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseOverrideHeaders(new OverrideHeaderOptions
|
||||
{
|
||||
ForwardedOptions = ForwardedHeaders.XForwardedProto
|
||||
});
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Equal("TestProtocol", context.Request.Scheme);
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.Add("X-Forwarded-Proto", "TestProtocol");
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AllForwardsDisabledByDefault()
|
||||
{
|
||||
var options = new OverrideHeaderOptions();
|
||||
Assert.True(options.ForwardedOptions == 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AllForwardsEnabledChangeRequestRemoteIpHostandProtocol()
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseOverrideHeaders(new OverrideHeaderOptions
|
||||
{
|
||||
ForwardedOptions = ForwardedHeaders.All
|
||||
});
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Equal("11.111.111.11", context.Connection.RemoteIpAddress.ToString());
|
||||
Assert.Equal("testhost", context.Request.Host.ToString());
|
||||
Assert.Equal("Protocol", context.Request.Scheme);
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.Add("X-Forwarded-For", "11.111.111.11");
|
||||
req.Headers.Add("X-Forwarded-Host", "testhost");
|
||||
req.Headers.Add("X-Forwarded-Proto", "Protocol");
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AllOptionsDisabledRequestDoesntChange()
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseOverrideHeaders(new OverrideHeaderOptions
|
||||
{
|
||||
ForwardedOptions = ForwardedHeaders.None
|
||||
});
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Null(context.Connection.RemoteIpAddress);
|
||||
Assert.Equal("localhost", context.Request.Host.ToString());
|
||||
Assert.Equal("http", context.Request.Scheme);
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.Add("X-Forwarded-For", "11.111.111.11");
|
||||
req.Headers.Add("X-Forwarded-Host", "otherhost");
|
||||
req.Headers.Add("X-Forwarded-Proto", "Protocol");
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PartiallyEnabledForwardsPartiallyChangesRequest()
|
||||
{
|
||||
var assertsExecuted = false;
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseOverrideHeaders(new OverrideHeaderOptions
|
||||
{
|
||||
ForwardedOptions = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
|
||||
});
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Equal("11.111.111.11", context.Connection.RemoteIpAddress.ToString());
|
||||
Assert.Equal("localhost", context.Request.Host.ToString());
|
||||
Assert.Equal("Protocol", context.Request.Scheme);
|
||||
assertsExecuted = true;
|
||||
return Task.FromResult(0);
|
||||
|
||||
});
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
req.Headers.Add("X-Forwarded-For", "11.111.111.11");
|
||||
req.Headers.Add("X-Forwarded-Proto", "Protocol");
|
||||
await server.CreateClient().SendAsync(req);
|
||||
Assert.True(assertsExecuted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
"warningsAsErrors": true
|
||||
},
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.HttpOverrides": "0.1.0-*",
|
||||
"Microsoft.AspNetCore.HttpOverrides": "1.0.0-*",
|
||||
"Microsoft.AspNetCore.TestHost": "1.0.0-*",
|
||||
"Microsoft.Extensions.Logging.Testing": "1.0.0-*",
|
||||
"xunit": "2.1.0"
|
||||
|
|
|
|||
Loading…
Reference in New Issue