在 ASP.NET Core 的建议配置中,应用使用用于 IIS 的 ASP.NET Core 模块 (ANCM)、Nginx 或 Apache 进行托管。 代理服务器、负载均衡器和其他网络设备通常会在请求到达应用之前隐藏有关请求的信息:

  • 当通过 HTTP 代理 HTTPS 请求时,原方案 (HTTPS) 将丢失,并且必须在标头中转接。
  • 由于应用收到来自代理的请求,而不是 Internet 或公司网络上请求的真实源,因此原始客户端 IP 地址也必须在标头中转接。

此信息在请求处理中可能很重要,例如在重定向、身份验证、链接生成、策略评估和客户端地理位置中。

对于面向 Web 场运行的应用,应阅读 Web 场中的主机 ASP.NET Core。

转接头

按照约定,代理转接 HTTP 标头中的信息。

X-Forwarded-Prefix

转接头中间件,ForwardedHeadersMiddleware,读取这些标头并填充 HttpContext 上的关联字段。

中间件更新:

有关上述内容的更多信息,请参阅该 GitHub 问题。

可以配置转接头中间件默认设置。 对于默认设置:

X-Forwarded-ForX-Forwarded-ProtoX-Forwarded-HostX-Forwarded-PrefixForwardedHeadersForwardedHeaders.None
X-Forwarded-ForX-Forwarded-ProtoX-Forwarded-ForX-Forwarded-Proto

IIS/IIS Express 和 ASP.NET Core 模块

X-Forwarded-ForX-Forwarded-Proto

其他代理服务器和负载均衡器方案

除在进程外托管时使用 IIS 集成之外,不会默认启用转接头中间件。 必须启用应用的转接头中间件才能处理带有 UseForwardedHeaders 的转接头。 启用中间件后,如果没有为中间件指定 ForwardedHeadersOptions,那么默认的 ForwardedHeadersOptions 是 ForwardedHeaders.None。

X-Forwarded-ForX-Forwarded-Proto

转接头中间件顺序

转接头中间件应在其他中间件之前运行。 此顺序可确保依赖于转接头信息的中间件可以使用标头值进行处理。 转接头中间件可以在诊断和错误处理之后运行,但必须在调用 UseHsts 之前运行:

using Microsoft.AspNetCore.HttpOverrides;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders =
        ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseForwardedHeaders();
    app.UseHsts();
}
else
{
    app.UseDeveloperExceptionPage();
    app.UseForwardedHeaders();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthorization();

app.MapRazorPages();

app.Run();
UseForwardedHeaders
using Microsoft.AspNetCore.HttpOverrides;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders =
        ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});

var app = builder.Build();

app.UseForwardedHeaders();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

Nginx 配置

X-Forwarded-ForX-Forwarded-ProtoX-Forwarded-For

Apache 配置

配置 ASP.NET Core 以使用代理服务器和负载均衡器
X-Forwarded-For

转接头中间件选项

ForwardedHeadersOptions 控制ForwardedHeadersOptions的行为。 以下示例更改了默认值:

2127.0.10.1X-Forwarded-ForX-Forwarded-For-My-Custom-Header-Name
using System.Net;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardLimit = 2;
    options.KnownProxies.Add(IPAddress.Parse("127.0.10.1"));
    options.ForwardedForHeaderName = "X-Forwarded-For-My-Custom-Header-Name";
});

var app = builder.Build();

app.UseForwardedHeaders();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthorization();

app.MapRazorPages();

app.Run();
X-Forwarded-Host**.contoso.comfoo.contoso.comcontoso.com[ABCD:EF01:2345:6789:ABCD:EF01:2345:6789]IListX-Forwarded-ForX-Forwarded-ForForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProtoX-Forwarded-HostX-Forwarded-HostX-Forwarded-ProtoX-Forwarded-ProtonullKnownProxiesKnownNetworksnull1ForwardLimit110.0.0.1::ffff:10.0.0.1IListnew IPNetwork(IPAddress.Loopback, 8)KnownProxies10.0.0.1::ffff:10.0.0.1IListIPAddress.IPv6LoopbackX-Original-ForX-Original-HostX-Original-Prototruefalse

方案与使用案例

无法添加转接头并且所有请求都安全的情况

在某些情况下,可能无法将转接头添加到代理到应用的请求中。 如果代理强制将所有公共外部请求执行为 HTTPS,则在使用任何类型的中间件之前,可以手动设置该方案:

using Microsoft.AspNetCore.HttpOverrides;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders =
        ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});

var app = builder.Build();

app.Use((context, next) =>
{
    context.Request.Scheme = "https";
    return next(context);
});

app.UseForwardedHeaders();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

此代码可以通过环境变量或者开发环境或过渡环境中的其他配置设置来禁用:

using Microsoft.AspNetCore.HttpOverrides;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders =
        ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});

var app = builder.Build();

if (!app.Environment.IsProduction())
{
    app.Use((context, next) =>
    {
        context.Request.Scheme = "https";
        return next(context);
    });
}

app.UseForwardedHeaders();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

处理改变请求路径的基路径和代理

一些代理可完整地传递路径,但是会删除应用基路径以便路由可正常工作。 UsePathBaseExtensions.UsePathBase 中间件将路径拆分为 HttpRequest.Path,将应用基路径拆分为 HttpRequest.PathBase。

/foo/foo/api/1Request.PathBase/fooRequest.Path/api/1
app.UsePathBase("/foo");
// ...
app.UseRouting();

当再次反向调用中间件时,将重新应用原始路径和基路径。 有关中间件处理顺序的详细信息,请参阅 ASP.NET Core 中间件。

/foo/api/1/api/1/foo/api/1
app.Use((context, next) =>
{
    context.Request.PathBase = new PathString("/foo");
    return next(context);
});

如果代理要添加路径数据,请使用 StartsWithSegments 并分配给 Path 属性,从而放弃部分路径以修复重定向和链接:

app.Use((context, next) =>
{
    if (context.Request.Path.StartsWithSegments("/foo", out var remainder))
    {
        context.Request.Path = remainder;
    }

    return next(context);
});

配置使用不同标头名称的代理

X-Forwarded-ForX-Forwarded-Proto
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedForHeaderName = "HeaderNamUsedByProxy_X-Forwarded-For_Header";
    options.ForwardedProtoHeaderName = "HeaderNamUsedByProxy_X-Forwarded-Proto_Header";
});

var app = builder.Build();

app.UseForwardedHeaders();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

转发 Linux 和非 IIS 反向代理的方案

如果将站点部署到 Azure Linux 应用服务、Azure Linux 虚拟机 (VM),或者部署到除 IIS 之外的任何其他反向代理之后,调用 UseHttpsRedirection 和 UseHsts 的应用都会使站点进入无限循环。 反向代理终止 TLS,并且 Kestrel 未发现正确的请求方案。 由于 OAuth 和 OIDC 生成了错误的重定向,因此它们在此配置中也会出现故障。 UseIISIntegration 在 IIS 之后运行时会添加和配置转接头中间件,但 Linux(Apache 或 Nginx 集成)没有匹配的自动配置。

ASPNETCORE_FORWARDEDHEADERS_ENABLEDtrueKnownProxies option

转发证书

Azure

若要为证书转发配置 Azure 应用服务,请参阅为 Azure 应用服务配置 TLS 相互身份验证。 以下指南与配置 ASP.NET Core 应用相关。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddCertificateForwarding(options =>
    options.CertificateHeader = "X-ARR-ClientCert");

var app = builder.Build();

app.UseCertificateForwarding();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthorization();
app.UseAuthentication();

app.MapRazorPages();

app.Run();

其他 Web 代理

如果使用的代理不是 IIS 或 Azure 应用服务的应用程序请求路由 (ARR),请配置代理,以便转发其在 HTTP 标头中收到的证书。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddCertificateForwarding(options =>
    options.CertificateHeader = "YOUR_CERTIFICATE_HEADER_NAME");

var app = builder.Build();

app.UseCertificateForwarding();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthorization();
app.UseAuthentication();

app.MapRazorPages();

app.Run();
HeaderConverter
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddCertificateForwarding(options =>
{
    options.CertificateHeader = "YOUR_CUSTOM_HEADER_NAME";
    options.HeaderConverter = (headerValue) =>
    {
        // Conversion logic to create an X509Certificate2.
        var clientCertificate = ConversionLogic.CreateAnX509Certificate2();
        return clientCertificate;
    };
});

var app = builder.Build();

app.UseCertificateForwarding();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthorization();
app.UseAuthentication();

app.MapRazorPages();

app.Run();

疑难解答

debugdebug
using Microsoft.AspNetCore.HttpLogging;
using Microsoft.AspNetCore.HttpOverrides;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddHttpLogging(options =>
{
    options.LoggingFields = HttpLoggingFields.RequestPropertiesAndHeaders;
});

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders =
        ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});

var app = builder.Build();

app.UseForwardedHeaders();
app.UseHttpLogging();

app.Use(async (context, next) =>
{
    // Connection: RemoteIp
    app.Logger.LogInformation("Request RemoteIp: {RemoteIpAddress}",
        context.Connection.RemoteIpAddress);

    await next(context);
});

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthorization();

app.MapRazorPages();

app.Run();
ForwardLimit1ForwardLimit

在处理转接头之前,请求的原始远程 IP 必须与 KnownProxies 或 KnownNetworks 列表中的条目匹配。 这通过不接受来自不受信任的代理的转发器来限制标头欺骗。 检测到未知代理时,日志记录会指出代理的地址:

September 20th 2018, 15:49:44.168 Unknown proxy: 10.0.0.100:54321
KnownProxiesKnownNetworks
using Microsoft.AspNetCore.HttpOverrides;
using System.Net;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders =
        ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
    options.KnownProxies.Add(IPAddress.Parse("10.0.0.100"));
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseForwardedHeaders();
    app.UseHsts();
}
else
{
    app.UseDeveloperExceptionPage();
    app.UseForwardedHeaders();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthorization();

app.MapRazorPages();

app.Run();
"Microsoft.AspNetCore.HttpLogging": "Information"appsettings.Development.json
{
  "DetailedErrors": true,
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.AspNetCore.HttpLogging": "Information"
    }
  }
}

其他资源

阅读剩余 0%
本站所有文章资讯、展示的图片素材等内容均为注册用户上传(部分报媒/平媒内容转载自网络合作媒体),仅供学习参考。 用户通过本站上传、发布的任何内容的知识产权归属用户或原始著作权人所有。如有侵犯您的版权,请联系我们反馈本站将在三个工作日内改正。