Поделиться через


Настройка ASP.NET Core для работы с прокси-серверами и подсистемами балансировки нагрузки

Примечание.

Это не последняя версия этой статьи. В текущей версии см. версию .NET 10 этой статьи.

Предупреждение

Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в политике поддержки .NET и .NET Core. В текущей версии см. версию .NET 10 этой статьи.

Автор: Крис Росс (Chris Ross)

В рекомендуемой конфигурации для ASP.NET Core приложение размещается с использованием модуля ASP.NET Core (ANCM) для IIS, Nginx или Apache. Прокси-серверы, подсистемы балансировки нагрузки и другие сетевые устройства часто скрывают сведения о запросах, еще не достигших целевого приложения.

  • Если прокси-сервер передает HTTPS-запросы через протокол HTTP, исходная схема (HTTPS) теряется и ее нужно дополнительно передать в заголовке.
  • Так как приложение получает запрос от прокси-сервера, а не от правильного источника в Интернете или корпоративной сети, исходный IP-адрес клиента также нужно передать в заголовке.

Эти сведения могут быть важны при обработке запросов, например для перенаправления, аутентификации, создания ссылок, оценки политик и геолокации клиентов.

Приложения, предназначенные для запуска в веб-ферме, должны ознакомиться с Размещением ASP.NET Core в веб-ферме.

Пересылаемые заголовки

По действующему соглашению прокси-серверы передают данные в заголовках HTTP.

Верхний колонтитул Описание
X-Forwarded-For (XFF) Содержит сведения о клиенте, который создал этот запрос, и обо всех предыдущих узлах в цепочке прокси-серверов. Этот параметр может содержать IP-адреса (и номера портов, если потребуется). В цепочке прокси-серверов первый параметр обозначает клиента, отправившего исходный запрос. Затем следуют идентификаторы прокси. В списке параметров отсутствует последний прокси-сервер в цепочке. IP-адрес последнего прокси-сервера (и номер порта, если нужно) поступает в формате удаленного IP-адреса на транспортном уровне.
X-Forwarded-Proto (XFP) Значение исходной схемы передачи данных (HTTP/HTTPS). Здесь может быть указан целый список схем, если запрос прошел через несколько прокси-серверов.
X-Forwarded-Host (XFH) Исходное значение поля Host в заголовке запроса. Обычно прокси-серверы не изменяют заголовок Host. В рекомендациях Макрософт по безопасности CVE-2018-0787 представлены сведения о связанной с повышением привилегий уязвимости, которая затрагивает системы, где прокси-сервер не проверяет заголовок Host и не ограничивает его значения известными допустимыми значениями.
X-Forwarded-Prefix Исходный базовый путь, запрошенный клиентом. Этот заголовок может быть полезным для приложений для правильного создания URL-адресов, перенаправлений или ссылок обратно на клиент.

Промежуточное ПО перенаправления заголовков (middleware) (ForwardedHeadersMiddleware) считывает эти заголовки и заполняет соответствующие поля в HttpContext.

Это ПО промежуточного слоя обновляет следующие сведения:

  • HttpContext.Connection.RemoteIpAddress: задайте значение заголовка X-Forwarded-For. Дополнительные параметры влияют на значение RemoteIpAddress, которое устанавливает ПО промежуточного слоя. Дополнительные сведения см. в разделе Опции промежуточного слоя перенаправленных заголовков. Используемые значения удаляются из X-Forwarded-For, а старое значение HttpContext.Connection.RemoteIpAddress сохраняется в X-Original-For. Примечание. Этот процесс может повторяться несколько раз при наличии нескольких значений X-Forwarded-For/Proto/Host/Prefix, что приводит к перемещению X-Original-*нескольких значений, включая исходный RemoteIpAddress/Host/Scheme/PathBase.
  • HttpContext.Request.Scheme: установите, используя значение заголовка X-Forwarded-Proto. Используемое значение удаляется из X-Forwarded-Proto, а старое значение HttpContext.Request.Scheme сохраняется в X-Original-Proto.
  • HttpContext.Request.Host: установите с использованием значения заголовка X-Forwarded-Host. Используемое значение удаляется из X-Forwarded-Host, а старое значение HttpContext.Request.Host сохраняется в X-Original-Host.
  • HttpContext.Request.PathBase: установлено с использованием значения заголовка X-Forwarded-Prefix. Используемое значение удаляется из X-Forwarded-Prefix, а старое значение HttpContext.Request.PathBase сохраняется в X-Original-Prefix.

Дополнительные сведения о вышеупомянутом см. в этом вопросе на GitHub.

Для программного обеспечения промежуточного слоя перенаправления заголовков можно настроить параметры по умолчанию. Для параметров по умолчанию:

  • Между приложением и источником запросов существует только один прокси.
  • Для известных прокси-серверов и известных сетей настраиваются только петлевые адреса.
  • Переадресованные заголовки называются X-Forwarded-For, X-Forwarded-ProtoX-Forwarded-Hostи X-Forwarded-Prefix.
  • Параметр ForwardedHeaders имеет значение ForwardedHeaders.None, необходимо задать желаемые перенаправители, чтобы включить промежуточное ПО.

Не все сетевые устройства добавляют заголовки X-Forwarded-For и X-Forwarded-Proto без дополнительной настройки. Если запросы, поступающие в приложение через прокси-сервер, не содержат эти заголовки, обратитесь к руководствам изготовителя устройства. Если устройство использует имена заголовков, отличные от X-Forwarded-For и X-Forwarded-Proto, задайте параметрам ForwardedForHeaderName и ForwardedProtoHeaderName значения, совпадающие с именами заголовков, используемыми устройством. Дополнительные сведения см. в разделах Параметры промежуточного слоя для пересылки заголовков и Конфигурация для прокси-сервера, использующего другие имена заголовков.

IIS и IIS Express с модулем ASP.NET Core

Посредник интеграции IIS включает по умолчанию посредник для перенаправления заголовков при размещении приложения вне процесса за IIS и модулем ASP.NET Core для IIS (ANCM). При использовании модуля ASP.NET Core, ПО промежуточного слоя перенаправленных заголовков настраивается так, чтобы запускаться первым в конвейере промежуточного слоя с ограниченной конфигурацией, специально предназначенной для этого модуля. Ограниченная конфигурация обусловлена проблемами доверия, связанными с перенаправленными заголовками, например, подделкой IP-адресов. В ПО промежуточного слоя настраивается пересылка заголовков X-Forwarded-For и X-Forwarded-Proto, и оно может работать только с одним локальным прокси-сервером. Если требуется дополнительная конфигурация, изучите раздел Параметры ПО промежуточного слоя перенаправления заголовков.

Другие сценарии использования прокси-сервера и подсистемы балансировки нагрузки

Помимо использования интеграции IIS при размещении вне процесса, ПО промежуточного слоя перенаправления заголовков по умолчанию не включено. Промежуточное ПО для пересылаемых заголовков должно быть включено, чтобы приложение могло обрабатывать перенаправленные заголовки с UseForwardedHeaders. После включения промежуточного программного обеспечения, если для него не задан параметр ForwardedHeadersOptions, для свойства ForwardedHeadersOptions.ForwardedHeaders по умолчанию устанавливается значение ForwardedHeaders.None.

Настройте ПО промежуточного слоя с ForwardedHeadersOptions для перенаправления заголовков X-Forwarded-For и X-Forwarded-Proto.

Порядок выполнения middleware для перенаправленных заголовков

Промежуточное ПО для перенаправления заголовков должно выполняться перед другим промежуточным ПО. Такой порядок гарантирует, что ПО промежуточного слоя, полагающееся на сведения о перенаправленных заголовках, может использовать значения заголовков для обработки. ПО промежуточного слоя перенаправления заголовков может выполняться после диагностики и обработки ошибок, однако его необходимо выполнить перед вызовом 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();

Примечание.

Если заголовки ForwardedHeadersOptions не указаны или не применяются к методам расширения с UseForwardedHeaders, по умолчанию будут перенаправляться заголовки ForwardedHeaders.None. Свойство ForwardedHeaders должно быть настроено для перенаправления заголовков.

Конфигурация Nginx

Сведения о перенаправлении заголовков X-Forwarded-For и X-Forwarded-Proto см. в разделе Размещение ASP.NET Core в Linux с Nginx. Дополнительные сведения см. в разделе NGINX. Использование перенаправленного заголовка.

Конфигурация Apache

X-Forwarded-For добавляется автоматически. Дополнительные сведения см. в разделе Модуль mod_proxy в Apache: заголовки обратных прокси-запросов.

Параметры промежуточного ПО для перенаправления заголовков

ForwardedHeadersOptions управляет поведением Промежуточного ПО для перенаправления заголовков. В следующем примере показано изменение значений по умолчанию.

  • Ограничивает количество записей в перенаправленных заголовках до 2.
  • Добавляет известный адрес прокси сервера 127.0.10.1.
  • Изменяет имя перенаправленного заголовка с заданного по умолчанию X-Forwarded-For на X-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();
Вариант Описание
AllowedHosts Ограничивает хосты по заголовку X-Forwarded-Host до указанных значений.
  • Значения сравниваются по порядковым номерам без учета регистра.
  • Номера портов указывать не нужно.
  • Если список пуст, разрешены все узлы.
  • Подстановочный символ верхнего уровня * разрешает все непустые хосты.
  • Подстановочные знаки поддоменов допускаются, но не могут указывать на корневой домен. Например, *.contoso.com соответствует поддомену foo.contoso.com, но не корневому домену contoso.com.
  • Имена узлов в Юникоде разрешены, но для сопоставления они преобразуются в Punycode.
  • IPv6-адреса должны включать ограничивающие квадратные скобки и иметь стандартный формат (например, [ABCD:EF01:2345:6789:ABCD:EF01:2345:6789]). IPv6-адреса не обрабатываются по особым правилам для проверки логического равенства между различными форматами, и канонизация не осуществляется.
  • Отсутствие ограничения разрешенных хостов может позволить кибератакующему выполнять спуфинг ссылок, созданных службой.
Значение по умолчанию — пустой объект IList<string>.
ForwardedForHeaderName Заголовок, заданный этим свойством, используется вместо заголовка, указанного в параметре ForwardedHeadersDefaults.XForwardedForHeaderName. Этот параметр применяется в случае, если для перенаправления данных прокси-сервер или сервер пересылки не используют заголовок X-Forwarded-For, а используют другой заголовок.

Значение по умолчанию — X-Forwarded-For.
ForwardedHeaders Определяет, какие пересылщики должны обрабатываться. См. перечисление ForwardedHeaders для списка применимых полей. Обычно для этого свойства задаются такие значения: ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto.

Значение по умолчанию — ForwardedHeaders.None.
ForwardedHostHeaderName Заголовок, заданный этим свойством, используется вместо заголовка, указанного в параметре ForwardedHeadersDefaults.XForwardedHostHeaderName. Этот параметр применяется в случае, если для перенаправления данных прокси-сервер или сервер пересылки не используют заголовок X-Forwarded-Host, а используют другой заголовок.

Значение по умолчанию — X-Forwarded-Host.
ForwardedProtoHeaderName Заголовок, заданный этим свойством, используется вместо заголовка, указанного в параметре ForwardedHeadersDefaults.XForwardedProtoHeaderName. Этот параметр применяется в случае, если для перенаправления данных прокси-сервер или сервер пересылки не используют заголовок X-Forwarded-Proto, а используют другой заголовок.

Значение по умолчанию — X-Forwarded-Proto.
ForwardLimit Ограничивает количество записей в обрабатываемых заголовках. Установите значение null, чтобы отключить это ограничение, но только в том случае, если настроено свойство KnownProxies или KnownNetworks. Установка значения, отличного от null, является мерой предосторожности (но не гарантией), чтобы защититься от неправильно сконфигурированных прокси и злонамеренных запросов, поступающих через побочные каналы в сети.

Промежуточное программное обеспечение для пересылки заголовков обрабатывает их в обратном порядке: справа налево. Если используется значение по умолчанию (1), обрабатывается только крайнее правое значение из заголовков, если не увеличить значение ForwardLimit.

Значение по умолчанию — 1.
KnownNetworks Диапазоны адресов известных сетей, от которых можно принимать перенаправленные заголовки. Укажите диапазоны IP-адресов, используя нотацию CIDR (классовая бесклассовая маршрутизация).

Если сервер использует сокеты с двумя режимами, IPv4-адреса указываются в формате IPv6 (например, IPv4-адрес 10.0.0.1 в формате IPv6 выглядит так: ::ffff:10.0.0.1). См. статью о методе IPAddress.MapToIPv6. Определите, требуется ли этот формат, обращаясь к HttpContext.Connection.RemoteIpAddress.

Значение по умолчанию — IList<IPNetwork> с одной записью для new IPNetwork(IPAddress.Loopback, 8).
KnownProxies Адреса известных прокси-серверов, от которых можно принимать перенаправленные заголовки. Используйте KnownProxies, чтобы указывать на точные совпадения IP-адресов.

Если сервер использует сокеты с двумя режимами, IPv4-адреса указываются в формате IPv6 (например, IPv4-адрес 10.0.0.1 в формате IPv6 выглядит так: ::ffff:10.0.0.1). См. статью о методе IPAddress.MapToIPv6. Определите, требуется ли этот формат, проверив HttpContext.Connection.RemoteIpAddress.

Значение по умолчанию — IList<IPAddress> с одной записью для IPAddress.IPv6Loopback.
OriginalForHeaderName Заголовок, заданный этим свойством, используется вместо заголовка, указанного в параметре ForwardedHeadersDefaults.XOriginalForHeaderName.

Значение по умолчанию — X-Original-For.
OriginalHostHeaderName Заголовок, заданный этим свойством, используется вместо заголовка, указанного в параметре ForwardedHeadersDefaults.XOriginalHostHeaderName.

Значение по умолчанию — X-Original-Host.
OriginalProtoHeaderName Заголовок, заданный этим свойством, используется вместо заголовка, указанного в параметре ForwardedHeadersDefaults.XOriginalProtoHeaderName.

Значение по умолчанию — X-Original-Proto.
RequireHeaderSymmetry Требуется, чтобы количество значений заголовков было синхронизировано для всех обрабатываемых ForwardedHeadersOptions.ForwardedHeaders.

Значение по умолчанию в ASP.NET Core 1.x — true. Значение по умолчанию в ASP.NET Core 2.0 или более поздней версии — false.

Сценарии и варианты использования

Когда невозможно добавить перенаправленные заголовки и все запросы являются безопасными

В некоторых случаях нет возможности добавлять перенаправленные заголовки в запросы, передаваемые приложению через прокси-сервер. Если прокси-сервер требует схему 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/1, полученном от прокси-сервера, промежуточное ПО устанавливает для Request.PathBase значение /foo, а для Request.Path значение /api/1 с помощью следующей команды:

app.UsePathBase("/foo");
// ...
app.UseRouting();

Примечание.

При использовании WebApplication (см. раздел "Миграция с ASP.NET Core" в .NET 5 на .NET 6) app.UseRouting должен вызываться после UsePathBase того, чтобы по промежуточному слоям маршрутизации можно было наблюдать измененный путь перед сопоставлением маршрутов. В противном случае маршруты сопоставляются до того, как путь будет переписан, как описано в ASP.NET Core Middleware и Маршрутизации в ASP.NET Core.

Исходный путь и базовый путь снова применяются, когда промежуточное ПО вызывается в обратном порядке. Дополнительные сведения о процессе обработки заказов для промежуточного ПО см. в статье Промежуточное ПО ASP.NET Core.

Если прокси-сервер усекает путь (например, перенаправляет /foo/api/1 в /api/1), такие перенаправления и ссылки можно исправить, задав для запроса свойство PathBase:

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-For и X-Forwarded-Proto, задайте параметрам ForwardedForHeaderName и ForwardedProtoHeaderName значения, совпадающие с именами заголовков, используемыми прокси-сервером:

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.

Приложения, вызывающие UseHttpsRedirection и UseHsts, помещают сайт в бесконечный цикл при развертывании в Службе приложений Azure Linux, виртуальной машине Linux в Azure или за любыми другими обратными прокси-серверами, помимо IIS. TLS завершается обратным прокси-сервером, и Kestrel не знает о правильной схеме запроса. OAuth и OIDC также не работают в этой конфигурации, поскольку создают неверные перенаправления. UseIISIntegration добавляет и настраивает ПО промежуточного слоя переадресованных заголовков при работе за IIS, но для Linux (интеграция с Apache или Nginx) нет аналогичной автоматической конфигурации.

Для пересылки схемы от прокси в сценариях, не связанных с IIS, включите промежуточное программное обеспечение для заголовков, установив ASPNETCORE_FORWARDEDHEADERS_ENABLED в true. Предупреждение. Этот флаг использует параметры, предназначенные для облачных сред, и не включает такие функции, как KnownProxies option, для ограничения IP-адресов, с которых принимается переадресация.

Пересылка сертификатов

Лазурный

Сведения о настройке Службы приложений Azure для переадресации сертификатов см. в статье Configure TLS mutual authentication for Azure App Service (Настройка взаимной проверки подлинности TLS для Службы приложений Azure). Приведенные ниже инструкции применимы к настройке приложения 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();

Другие веб-прокси

Если вы используете прокси-сервер не IIS или маршрутизацию запросов приложений (ARR) Службы приложений Azure, настройте прокси-сервер для переадресации сертификата, полученного в заголовке 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();

Если прокси-сервер не выполняет шифрование сертификата в кодировке base64 (как в случае с Nginx), задайте параметр 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();

Устранение неполадок

Если заголовки не перенаправляются так, как ожидалось, включите журналирование на уровне debug и ведение журнала HTTP-запросов. UseHttpLogging нужно вызывать после UseForwardedHeaders:

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();

Если в некотором заголовке содержится несколько значений, промежуточное ПО перенаправления заголовков обрабатывает их в обратном порядке — справа налево. По умолчанию ForwardLimit имеет значение 1 (один), а значит обрабатывается только крайнее правое значение из заголовков, если не увеличить значение ForwardLimit.

Удаленный IP-адрес из исходного запроса должен совпадать с записью в списке KnownProxies или KnownNetworks, чтобы происходила обработка заголовков переадресации. Это ограничивает спуфинг заголовков, отклоняя пересылку от ненадежных прокси-серверов. В журнале указывается адрес неизвестного обнаруженного прокси-сервера:

September 20th 2018, 15:49:44.168 Unknown proxy: 10.0.0.100:54321

В приведенном выше примере указан прокси-сервер с адресом 10.0.0.100. Если сервер является доверенным прокси-сервером, добавьте его IP-адрес в KnownProxies (или добавьте доверенную сеть в KnownNetworks). Дополнительные сведения см. в разделе Параметры промежуточного слоя для заголовков перенаправления.

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"
    }
  }
}

Внимание

Переадресацию заголовков следует разрешить только доверенным прокси-серверам и сетям. В противном случае будут возможны атаки подмены IP-адресов.

Дополнительные ресурсы

В рекомендуемой конфигурации для ASP.NET Core приложение размещается с использованием модуля ASP.NET Core для IIS, Nginx или Apache. Прокси-серверы, подсистемы балансировки нагрузки и другие сетевые устройства часто скрывают сведения о запросах, еще не достигших целевого приложения.

  • Если прокси-сервер передает HTTPS-запросы через протокол HTTP, исходная схема (HTTPS) теряется и ее нужно дополнительно передать в заголовке.
  • Так как приложение получает запрос от прокси-сервера, а не от правильного источника в Интернете или корпоративной сети, исходный IP-адрес клиента также нужно передать в заголовке.

Эти сведения могут быть важны при обработке запросов, например для перенаправления, аутентификации, создания ссылок, оценки политик и геолокации клиентов.

Пересылаемые заголовки

По действующему соглашению прокси-серверы передают данные в заголовках HTTP.

Верхний колонтитул Описание
X-Forwarded-For Содержит сведения о клиенте, который создал этот запрос, и обо всех предыдущих узлах в цепочке прокси-серверов. Этот параметр может содержать IP-адреса (и номера портов, если потребуется). В цепочке прокси-серверов первый параметр обозначает клиента, отправившего исходный запрос. Затем следуют идентификаторы прокси. В списке параметров отсутствует последний прокси-сервер в цепочке. IP-адрес последнего прокси-сервера (и номер порта, если нужно) поступает в формате удаленного IP-адреса на транспортном уровне.
X-Forwarded-Proto Значение исходной схемы передачи данных (HTTP/HTTPS). Здесь может быть указан целый список схем, если запрос прошел через несколько прокси-серверов.
X-Forwarded-Host Исходное значение поля Host в заголовке запроса. Обычно прокси-серверы не изменяют заголовок Host. В рекомендациях Макрософт по безопасности CVE-2018-0787 представлены сведения о связанной с повышением привилегий уязвимости, которая затрагивает системы, где прокси-сервер не проверяет заголовок Host и не ограничивает его значения известными допустимыми значениями.

Промежуточное ПО для перенаправления заголовков (ForwardedHeadersMiddleware) считывает эти заголовки и заполняет соответствующие поля в HttpContext.

Это ПО промежуточного слоя обновляет следующие сведения:

  • HttpContext.Connection.RemoteIpAddress — устанавливается на основе значения заголовка X-Forwarded-For. Дополнительные параметры влияют на значение RemoteIpAddress, которое устанавливает ПО промежуточного слоя. Дополнительные сведения см. в разделе Опции промежуточного слоя перенаправленных заголовков. Потребленные значения удаляются из X-Forwarded-For, а старые значения сохраняются в X-Original-For. Тот же шаблон применяется к другим заголовкам Host и Proto.
  • HttpContext.Request.Scheme — устанавливается на основе значения заголовка X-Forwarded-Proto.
  • HttpContext.Request.Host — устанавливается на основе значения заголовка X-Forwarded-Host.

Дополнительные сведения о вышеупомянутом см. в этом вопросе на GitHub.

Для программного обеспечения промежуточного слоя перенаправления заголовков можно настроить параметры по умолчанию. Для параметров по умолчанию:

  • Между приложением и источником запросов существует только один прокси.
  • Для известных прокси-серверов и известных сетей настраиваются только петлевые адреса.
  • Перенаправленным заголовками заданы имена X-Forwarded-For и X-Forwarded-Proto.
  • Параметр ForwardedHeaders имеет значение ForwardedHeaders.None, необходимо задать желаемые перенаправители, чтобы включить промежуточное ПО.

Не все сетевые устройства добавляют заголовки X-Forwarded-For и X-Forwarded-Proto без дополнительной настройки. Если запросы, поступающие в приложение через прокси-сервер, не содержат эти заголовки, обратитесь к руководствам изготовителя устройства. Если устройство использует имена заголовков, отличные от X-Forwarded-For и X-Forwarded-Proto, задайте параметрам ForwardedForHeaderName и ForwardedProtoHeaderName значения, совпадающие с именами заголовков, используемыми устройством. Дополнительные сведения см. в разделах Параметры промежуточного слоя для пересылки заголовков и Конфигурация для прокси-сервера, использующего другие имена заголовков.

IIS и IIS Express с модулем ASP.NET Core

Промежуточное программное обеспечение интеграции с IIS по умолчанию включает промежуточное ПО для перенаправления заголовков, когда приложение размещается вне процесса за IIS и модулем ASP.NET Core. При использовании модуля ASP.NET Core ПО промежуточного слоя перенаправления заголовков настраивается так, чтобы оно запускалось первым в конвейере ПО промежуточного слоя и использовало специальную ограниченную конфигурацию, так как перенаправленные заголовки создают дополнительные проблемы с доверием (например, проблему IP-спуфинга). В ПО промежуточного слоя настраивается пересылка заголовков X-Forwarded-For и X-Forwarded-Proto, и оно может работать только с одним локальным прокси-сервером. Если требуется дополнительная конфигурация, изучите раздел Параметры ПО промежуточного слоя перенаправления заголовков.

Другие сценарии использования прокси-сервера и подсистемы балансировки нагрузки

Если не используется интеграция IIS для внепроцессного размещения, промежуточное ПО заголовков пересылки не включено по умолчанию. Промежуточное ПО для пересылаемых заголовков должно быть включено, чтобы приложение могло обрабатывать перенаправленные заголовки с UseForwardedHeaders. После включения промежуточного программного обеспечения, если для него не задан параметр ForwardedHeadersOptions, для свойства ForwardedHeadersOptions.ForwardedHeaders по умолчанию устанавливается значение ForwardedHeaders.None.

Настройте ПО промежуточного слоя с ForwardedHeadersOptions для перенаправления заголовков X-Forwarded-For и X-Forwarded-Proto в Startup.ConfigureServices.

Порядок выполнения middleware для перенаправленных заголовков

Промежуточное ПО для переадресации заголовков должно выполняться до другой промежуточной обработки. Такой порядок гарантирует, что ПО промежуточного слоя, полагающееся на сведения о перенаправленных заголовках, может использовать значения заголовков для обработки. Промежуточное ПО для переадресации заголовков может выполняться после диагностики и обработки ошибок, однако необходимо выполнить перед вызовом UseHsts.

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        services.Configure<ForwardedHeadersOptions>(options =>
        {
            options.ForwardedHeaders =
                ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
        });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseForwardedHeaders();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseForwardedHeaders();
            app.UseHsts();
        }

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

        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

Кроме того, можно вызвать UseForwardedHeaders перед диагностикой:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseForwardedHeaders();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

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

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

Примечание.

Если ForwardedHeadersOptions не указаны в Startup.ConfigureServices или непосредственно в метод расширения с UseForwardedHeaders, по умолчанию для перенаправления используются заголовки ForwardedHeaders.None. Свойство ForwardedHeaders должно быть настроено для перенаправления заголовков.

Конфигурация Nginx

Сведения о перенаправлении заголовков X-Forwarded-For и X-Forwarded-Proto см. в разделе Размещение ASP.NET Core в Linux с Nginx. Дополнительные сведения см. в разделе NGINX. Использование перенаправленного заголовка.

Конфигурация Apache

X-Forwarded-For добавляется автоматически (см. раздел Apache Module mod_proxy: заголовки обратных прокси-запросов).

Параметры промежуточного ПО для перенаправления заголовков

Параметр ForwardedHeadersOptions позволяет управлять поведением посредника обработки перенаправленных заголовков. В следующем примере показано изменение значений по умолчанию.

  • Ограничьте количество записей в перенаправленных заголовках до 2.
  • Добавьте известный адрес прокси сервера 127.0.10.1.
  • Измените имя перенаправленного заголовка с заданного по умолчанию X-Forwarded-For на X-Forwarded-For-My-Custom-Header-Name.
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";
});
Вариант Описание
AllowedHosts Ограничивает хосты по заголовку X-Forwarded-Host до указанных значений.
  • Значения сравниваются по порядковым номерам без учета регистра.
  • Номера портов указывать не нужно.
  • Если список пуст, разрешены все узлы.
  • Подстановочный символ верхнего уровня * разрешает все непустые хосты.
  • Подстановочные знаки поддоменов допускаются, но не могут указывать на корневой домен. Например, *.contoso.com соответствует поддомену foo.contoso.com, но не корневому домену contoso.com.
  • Имена узлов в Юникоде разрешены, но для сопоставления они преобразуются в Punycode.
  • IPv6-адреса должны включать ограничивающие квадратные скобки и иметь стандартный формат (например, [ABCD:EF01:2345:6789:ABCD:EF01:2345:6789]). IPv6-адреса не обрабатываются по особым правилам для проверки логического равенства между различными форматами, и канонизация не осуществляется.
  • Отсутствие ограничения разрешенных хостов может позволить кибератакующему выполнять спуфинг ссылок, созданных службой.
Значение по умолчанию — пустой объект IList<string>.
ForwardedHeaders Определяет, какие пересылщики должны обрабатываться. См. перечисление ForwardedHeaders для списка применимых полей. Обычно для этого свойства задаются такие значения: ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto.

Значение по умолчанию — ForwardedHeaders.None.
ForwardedForHeaderName Заголовок, заданный этим свойством, используется вместо заголовка, указанного в параметре ForwardedHeadersDefaults.XForwardedForHeaderName. Этот параметр применяется в случае, если для перенаправления данных прокси-сервер или сервер пересылки не используют заголовок X-Forwarded-For, а используют другой заголовок.

Значение по умолчанию — X-Forwarded-For.
ForwardedHostHeaderName Заголовок, заданный этим свойством, используется вместо заголовка, указанного в параметре ForwardedHeadersDefaults.XForwardedHostHeaderName. Этот параметр применяется в случае, если для перенаправления данных прокси-сервер или сервер пересылки не используют заголовок X-Forwarded-Host, а используют другой заголовок.

Значение по умолчанию — X-Forwarded-Host.
ForwardedProtoHeaderName Заголовок, заданный этим свойством, используется вместо заголовка, указанного в параметре ForwardedHeadersDefaults.XForwardedProtoHeaderName. Этот параметр применяется в случае, если для перенаправления данных прокси-сервер или сервер пересылки не используют заголовок X-Forwarded-Proto, а используют другой заголовок.

Значение по умолчанию — X-Forwarded-Proto.
ForwardedPrefixHeaderName Используйте заголовок, указанный этим свойством, вместо того, который указан в ForwardedHeadersDefaults.XForwardedPrefixHeaderName. Этот параметр применяется в случае, если для перенаправления данных прокси-сервер или сервер пересылки не используют заголовок X-Forwarded-Prefix, а используют другой заголовок.

Значение по умолчанию — X-Forwarded-Prefix.
ForwardLimit Ограничивает количество записей в обрабатываемых заголовках. Установите значение null, чтобы отключить это ограничение, но только в том случае, если настроено свойство KnownProxies или KnownNetworks. Установка значения, отличного от null, является мерой предосторожности (но не гарантией), чтобы защититься от неправильно сконфигурированных прокси и злонамеренных запросов, поступающих через побочные каналы в сети.

Промежуточное программное обеспечение для пересылки заголовков обрабатывает их в обратном порядке: справа налево. Если используется значение по умолчанию (1), обрабатывается только крайнее правое значение из заголовков, если не увеличить значение ForwardLimit.

Значение по умолчанию — 1.
KnownNetworks Диапазоны адресов известных сетей, от которых можно принимать перенаправленные заголовки. Укажите диапазоны IP-адресов, используя нотацию CIDR (классовая бесклассовая маршрутизация).

Если сервер использует сокеты с двумя режимами, IPv4-адреса указываются в формате IPv6 (например, IPv4-адрес 10.0.0.1 в формате IPv6 выглядит так: ::ffff:10.0.0.1). См. статью о методе IPAddress.MapToIPv6. Определите, требуется ли этот формат, проверив HttpContext.Connection.RemoteIpAddress.

Значение по умолчанию — IList<IPNetwork> с одной записью для new IPNetwork(IPAddress.Loopback, 8).
KnownProxies Адреса известных прокси-серверов, от которых можно принимать перенаправленные заголовки. Используйте KnownProxies, чтобы указывать на точные совпадения IP-адресов.

Если сервер использует сокеты с двумя режимами, IPv4-адреса указываются в формате IPv6 (например, IPv4-адрес 10.0.0.1 в формате IPv6 выглядит так: ::ffff:10.0.0.1). См. статью о методе IPAddress.MapToIPv6. Определите, требуется ли этот формат, проверив HttpContext.Connection.RemoteIpAddress.

Значение по умолчанию — IList<IPAddress> с одной записью для IPAddress.IPv6Loopback.
OriginalForHeaderName Заголовок, заданный этим свойством, используется вместо заголовка, указанного в параметре ForwardedHeadersDefaults.XOriginalForHeaderName.

Значение по умолчанию — X-Original-For.
OriginalHostHeaderName Заголовок, заданный этим свойством, используется вместо заголовка, указанного в параметре ForwardedHeadersDefaults.XOriginalHostHeaderName.

Значение по умолчанию — X-Original-Host.
OriginalProtoHeaderName Заголовок, заданный этим свойством, используется вместо заголовка, указанного в параметре ForwardedHeadersDefaults.XOriginalProtoHeaderName.

Значение по умолчанию — X-Original-Proto.
OriginalPrefixHeaderName Используйте заголовок, указанный этим свойством вместо того, который указан в ForwardedHeadersDefaults.XOriginalPrefixHeaderName.

Значение по умолчанию — X-Original-Prefix.
RequireHeaderSymmetry Требуется, чтобы количество значений заголовков было синхронизировано для всех обрабатываемых ForwardedHeadersOptions.ForwardedHeaders.

Значение по умолчанию в ASP.NET Core 1.x — true. Значение по умолчанию в ASP.NET Core 2.0 или более поздней версии — false.

Сценарии и варианты использования

Когда невозможно добавить перенаправленные заголовки и все запросы являются безопасными

В некоторых случаях нет возможности добавлять перенаправленные заголовки в запросы, передаваемые приложению через прокси-сервер. Если прокси-сервер требует схему HTTPS для всех внешних запросов, ее можно задать вручную в Startup.Configure перед использованием любого ПО промежуточного слоя:

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

В тестовой среде или среде разработки этот код можно отключить с помощью переменной среды или другого параметра конфигурации.

Обрабатывайте базовый путь и прокси-серверы, которые изменяют путь запроса

Некоторые прокси-серверы передают путь, но добавляют к нему базовый путь приложения, который нужно удалять для правильной работы маршрутизации. ПО промежуточного слоя UsePathBaseExtensions.UsePathBase разделяет путь на два сегмента: HttpRequest.Path и базовый путь приложения HttpRequest.PathBase.

Если /foo является базовым путём приложения в пути /foo/api/1, полученном от прокси-сервера, промежуточное ПО устанавливает для Request.PathBase значение /foo, а для Request.Path значение /api/1 с помощью следующей команды:

app.UsePathBase("/foo");

Исходный путь и базовый путь снова применяются, когда промежуточное ПО вызывается в обратном порядке. Дополнительные сведения о процессе обработки заказов для промежуточного ПО см. в статье Промежуточное ПО ASP.NET Core.

Если прокси-сервер усекает путь (например, перенаправляет /foo/api/1 в /api/1), такие перенаправления и ссылки можно исправить, задав для запроса свойство PathBase:

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

Если прокси-сервер добавляет к пути данные, устраните лишнюю часть пути, чтобы исправить перенаправления и ссылки, используя StartsWithSegments и присвоив значение свойству Path:

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

    return next();
});

Конфигурация для прокси-сервера, использующего другие имена заголовков

Если для перенаправления адреса или порта прокси-сервера и сведений об исходной схеме прокси-сервер не использует заголовки с именами X-Forwarded-For и X-Forwarded-Proto, задайте параметрам ForwardedForHeaderName и ForwardedProtoHeaderName значения, совпадающие с именами заголовков, используемыми прокси-сервером:

services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedForHeaderName = "Header_Name_Used_By_Proxy_For_X-Forwarded-For_Header";
    options.ForwardedProtoHeaderName = "Header_Name_Used_By_Proxy_For_X-Forwarded-Proto_Header";
});

Переслать схему для систем на Linux и обратных прокси-серверов, не относящихся к IIS.

Приложения, вызывающие UseHttpsRedirection и UseHsts, помещают сайт в бесконечный цикл при развертывании в Службе приложений Azure Linux, виртуальной машине Linux в Azure или за любыми другими обратными прокси-серверами, помимо IIS. TLS завершается обратным прокси-сервером, и Kestrel не знает о правильной схеме запроса. OAuth и OIDC также не работают в этой конфигурации, поскольку создают неверные перенаправления. UseIISIntegration добавляет и настраивает ПО промежуточного слоя переадресованных заголовков при работе за IIS, но для Linux (интеграция с Apache или Nginx) нет аналогичной автоматической конфигурации.

Для переадресации схемы с прокси-сервера вне IIS добавьте и настройте Middleware перенаправленных заголовков. В Startup.ConfigureServices используйте следующий код:

// using Microsoft.AspNetCore.HttpOverrides;

if (string.Equals(
    Environment.GetEnvironmentVariable("ASPNETCORE_FORWARDEDHEADERS_ENABLED"),
    "true", StringComparison.OrdinalIgnoreCase))
{
    services.Configure<ForwardedHeadersOptions>(options =>
    {
        options.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
            ForwardedHeaders.XForwardedProto;
        // Only loopback proxies are allowed by default.
        // Clear that restriction because forwarders are enabled by explicit
        // configuration.
        options.KnownNetworks.Clear();
        options.KnownProxies.Clear();
    });
}

Пересылка сертификатов

Лазурный

Сведения о настройке Службы приложений Azure для переадресации сертификатов см. в статье Configure TLS mutual authentication for Azure App Service (Настройка взаимной проверки подлинности TLS для Службы приложений Azure). Приведенные ниже инструкции применимы к настройке приложения ASP.NET Core.

В Startup.Configure добавьте указанный ниже код перед вызовом app.UseAuthentication();:

app.UseCertificateForwarding();

Настройте промежуточное программное обеспечение перенаправления сертификатов, чтобы указать имя заголовка, который использует Azure. В Startup.ConfigureServices добавьте указанный ниже код, чтобы настроить заголовок, из которого ПО промежуточного слоя создает сертификат:

services.AddCertificateForwarding(options =>
    options.CertificateHeader = "X-ARR-ClientCert");

Другие веб-прокси

Если вы используете прокси-сервер не IIS или маршрутизацию запросов приложений (ARR) Службы приложений Azure, настройте прокси-сервер для переадресации сертификата, полученного в заголовке HTTP. В Startup.Configure добавьте указанный ниже код перед вызовом app.UseAuthentication();:

app.UseCertificateForwarding();

Настройте посредническое программное обеспечение для пересылки сертификатов, чтобы задать имя заголовка. В Startup.ConfigureServices добавьте указанный ниже код, чтобы настроить заголовок, из которого ПО промежуточного слоя создает сертификат:

services.AddCertificateForwarding(options =>
    options.CertificateHeader = "YOUR_CERTIFICATE_HEADER_NAME");

Если прокси-сервер не выполняет шифрование сертификата в кодировке base64 (как в случае с Nginx), задайте параметр HeaderConverter. Рассмотрим следующий пример в Startup.ConfigureServices:

services.AddCertificateForwarding(options =>
{
    options.CertificateHeader = "YOUR_CUSTOM_HEADER_NAME";
    options.HeaderConverter = (headerValue) =>
    {
        var clientCertificate =
           /* some conversion logic to create an X509Certificate2 */
        return clientCertificate;
    }
});

Устранение неполадок

Если заголовки не перенаправляются должным образом, включите логирование. Если журналы не содержат достаточно информации для устранения неполадок, просмотрите список заголовков в запросе, полученном сервером. Используйте встроенное ПО промежуточного слоя для записи заголовков запроса в ответ приложения или для сохранения заголовков в журнал.

Чтобы записать заголовки в ответ приложения, используйте следующее встроенное ПО промежуточного слоя на терминале сразу после вызова UseForwardedHeaders в Startup.Configure:

app.Run(async (context) =>
{
    context.Response.ContentType = "text/plain";

    // Request method, scheme, and path
    await context.Response.WriteAsync(
        $"Request Method: {context.Request.Method}{Environment.NewLine}");
    await context.Response.WriteAsync(
        $"Request Scheme: {context.Request.Scheme}{Environment.NewLine}");
    await context.Response.WriteAsync(
        $"Request Path: {context.Request.Path}{Environment.NewLine}");

    // Headers
    await context.Response.WriteAsync($"Request Headers:{Environment.NewLine}");

    foreach (var header in context.Request.Headers)
    {
        await context.Response.WriteAsync($"{header.Key}: " +
            $"{header.Value}{Environment.NewLine}");
    }

    await context.Response.WriteAsync(Environment.NewLine);

    // Connection: RemoteIp
    await context.Response.WriteAsync(
        $"Request RemoteIp: {context.Connection.RemoteIpAddress}");
});

Можно сделать запись в журналы, а не в текст ответа. Запись в журналы позволяет сайту нормально работать во время отладки.

Для записи в журналы, а не в текст ответа, сделайте следующее:

app.Use(async (context, next) =>
{
    // Request method, scheme, path, and base path
    _logger.LogDebug("Request Method: {Method}", context.Request.Method);
    _logger.LogDebug("Request Scheme: {Scheme}", context.Request.Scheme);
    _logger.LogDebug("Request Path: {Path}", context.Request.Path);
    _logger.LogDebug("Request Path Base: {PathBase}", context.Request.PathBase);

    // Headers
    foreach (var header in context.Request.Headers)
    {
        _logger.LogDebug("Header: {Key}: {Value}", header.Key, header.Value);
    }

    // Connection: RemoteIp
    _logger.LogDebug("Request RemoteIp: {RemoteIpAddress}",
        context.Connection.RemoteIpAddress);

    await next();
});

После обработки значения X-Forwarded-{For|Proto|Host|Prefix} перемещаются в X-Original-{For|Proto|Host|Prefix}. Если в некотором заголовке содержится несколько значений, промежуточное ПО перенаправления заголовков обрабатывает их в обратном порядке — справа налево. По умолчанию ForwardLimit имеет значение 1 (один), а значит обрабатывается только крайнее правое значение из заголовков, если не увеличить значение ForwardLimit.

Удаленный IP-адрес из исходного запроса должен совпадать с записью в списке KnownProxies или KnownNetworks, чтобы происходила обработка заголовков переадресации. Это ограничивает спуфинг заголовков, отклоняя пересылку от ненадежных прокси-серверов. В журнале указывается адрес неизвестного обнаруженного прокси-сервера:

September 20th 2018, 15:49:44.168 Unknown proxy: 10.0.0.100:54321

В приведенном выше примере указан прокси-сервер с адресом 10.0.0.100. Если сервер является доверенным прокси-сервером, добавьте его IP-адрес в KnownProxies (или добавьте доверенную сеть в KnownNetworks) в файле Startup.ConfigureServices. Дополнительные сведения см. в разделе Параметры промежуточного слоя для заголовков перенаправления.

services.Configure<ForwardedHeadersOptions>(options =>
{
    options.KnownProxies.Add(IPAddress.Parse("10.0.0.100"));
});

Внимание

Переадресацию заголовков следует разрешить только доверенным прокси-серверам и сетям. В противном случае будут возможны атаки подмены IP-адресов.

Дополнительные ресурсы