Configurer ASP.NET Core pour l’utilisation de serveurs proxy et d’équilibreurs de charge

Par Chris Ross

Dans la configuration recommandée pour ASP.NET Core, l’application est hébergée à l’aide de ASP.NET Core Module (ANCM) pour IIS, Nginx ou Apache. Les serveurs proxy, les équilibreurs de charge et autres dispositifs réseau masquent souvent les informations sur la requête avant qu’elle n’atteigne l’application :

  • Quand les requêtes HTTPS sont transmises par proxy via HTTP, le schéma d’origine (HTTPS) est perdu et doit être transféré dans un en-tête.
  • Étant donné qu’une requête adressée à une application transite par le proxy au lieu de provenir directement de sa source sur Internet ou le réseau d’entreprise, l’adresse IP cliente d’origine doit également être transférée dans un en-tête.

Ces informations peuvent être importantes pour traiter les requêtes, par exemple dans les redirections, l’authentification, la génération de lien, l’évaluation des stratégies et la géolocalisation des clients.

En-têtes transférés

Par convention, les proxys transfèrent les informations dans des en-têtes HTTP.

En-tête Description
X-Forwarded-For (XFF) Contient des informations sur le client à l’origine de la requête et les proxys suivants dans une chaîne de proxys. Ce paramètre peut contenir des adresses IP et, éventuellement, des numéros de port. Dans une chaîne de serveurs proxy, le premier paramètre indique le client sur lequel la requête a été effectuée pour la première fois. Viennent ensuite les identificateurs des proxy suivants. Le dernier proxy dans la chaîne ne figure pas dans la liste des paramètres. L’adresse IP du dernier proxy et éventuellement un numéro de port sont disponibles en tant qu’adresse IP distante au niveau de la couche transport.
X-Forwarded-Proto (XFP) Valeur du schéma d’origine, HTTP ou HTTPS. La valeur peut également être une liste de schémas si la requête a franchi plusieurs proxys.
X-Forwarded-Host (XFH) Valeur d’origine du champ d’en-tête de l’hôte. En règle générale, les proxys ne modifient pas l’en-tête de l’hôte. Consultez le document Microsoft Security Advisory CVE-2018-0787 pour plus d’informations sur une vulnérabilité par élévation de privilèges qui affecte les systèmes où le proxy ne valide pas les en-têtes d’hôte ou ne les limite pas à des valeurs correctes connues.

L’intergiciel Des en-têtes transférés, ForwardedHeadersMiddlewarelit ces en-têtes et remplit les champs associés sur HttpContext.

Le middleware met à jour les éléments suivants :

Pour plus d’informations sur le problème précédent, consultez ce problème GitHub.

Vous pouvez configurer les paramètres par défaut du middleware des en-têtes transférés. Pour les paramètres par défaut :

  • Il n’existe qu’un seul proxy entre l’application et la source des demandes.
  • Seules des adresses de bouclage sont configurées pour les proxys connus et les réseaux connus.
  • Les en-têtes transférés sont nommés X-Forwarded-For et X-Forwarded-Proto.
  • La ForwardedHeaders valeur est ForwardedHeaders.None, les redirecteurs souhaités doivent être définis ici pour activer le middleware.

Certaines appliances réseau nécessitent une configuration supplémentaire pour ajouter les en-têtes X-Forwarded-For et X-Forwarded-Proto. Consultez les instructions du fabricant de votre appliance si des requêtes en proxy ne contiennent pas ces en-têtes quand elles atteignent l’application. Si l’appliance utilise des noms d’en-têtes autres que X-Forwarded-For et X-Forwarded-Proto, définissez les options ForwardedForHeaderName et ForwardedProtoHeaderName pour faire correspondre les noms d’en-têtes utilisés par l’appliance. Pour plus d’informations, consultez Options du middleware des en-têtes transférés et Configuration d’un proxy qui utilise des noms d’en-têtes différents.

Module ASP.NET Core et IIS/IIS Express

Le middleware d’en-têtes transférés est activé par défaut par le middleware d’intégration IIS lorsque l’application est hébergée hors processus derrière IIS et le module ASP.NET Core (ANCM) pour IIS. L’intergiciel des en-têtes transférés est activé pour s’exécuter en premier dans le pipeline d’intergiciels avec une configuration restreinte spécifique au module ASP.NET Core. La configuration restreinte est due à des problèmes d’approbation avec les en-têtes transférés, par exemple l’usurpation d’adresse IP. Le middleware est configuré pour transférer les en-têtes X-Forwarded-For et X-Forwarded-Proto et est limité à un proxy localhost unique. Si une configuration supplémentaire est requise, consultez les options du middleware des en-têtes transférés.

Autres scénarios avec un serveur proxy et un équilibreur de charge

En dehors de l’utilisation de l’intégration IIS lors de l’hébergement hors processus, l’intergiciel des en-têtes transférés n’est pas activé par défaut. L’intergiciel des en-têtes transférés doit être activé pour qu’une application puisse traiter les en-têtes transférés avec UseForwardedHeaders. Une fois l’intergiciel activé, si aucune option ForwardedHeadersOptions ne lui est spécifiée, les valeurs par défaut ForwardedHeadersOptions.ForwardedHeaders ont pour valeur ForwardedHeaders.None.

Configurez l’intergiciel avec ForwardedHeadersOptions pour transférer les en-têtes et X-Forwarded-Proto les X-Forwarded-For en-têtes.

Ordre du middleware Forwarded Headers

L’intergiciel des en-têtes transférés doit s’exécuter avant d’autres middlewares. Cet ordre permet au middleware qui repose sur les informations des en-têtes transférés d’utiliser les valeurs d’en-tête pour le traitement. Le middleware des en-têtes transférés peut s’exécuter après la gestion des diagnostics et des erreurs, mais il doit être exécuté avant d’appeler 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();

Vous pouvez également appeler UseForwardedHeaders avant les diagnostics :

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

Notes

Si aucune valeur n’est ForwardedHeadersOptions spécifiée ou appliquée directement à la méthode d’extension avec UseForwardedHeaders, les en-têtes par défaut à transférer sont ForwardedHeaders.None. La propriété ForwardedHeaders doit être configurée avec les en-têtes à transférer.

Configuration de Nginx

Pour transférer les en-têtes et X-Forwarded-Proto les X-Forwarded-For en-têtes, consultez host ASP.NET Core sur Linux avec Nginx. Pour plus d’informations, consultez NGINX: Using the Forwarded header (NGINX : utilisation de l’en-tête Forwarded).

Configuration d’Apache

X-Forwarded-For est ajouté automatiquement. Pour plus d’informations, consultez le mod_proxy du module Apache : en-têtes de demande de proxy inverse. Pour plus d’informations sur la façon de transférer l’en-têteX-Forwarded-Proto, consultez Host ASP.NET Core sur Linux avec Apache.

Options du middleware des en-têtes transférés

ForwardedHeadersOptions contrôle le comportement du middleware des en-têtes transférés. L’exemple suivant change les valeurs par défaut :

  • Limite le nombre d’entrées dans les en-têtes transférés à 2.
  • Ajoute une adresse proxy connue de 127.0.10.1.
  • Modifie le nom d’en-tête transféré de la valeur par défaut X-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();
Option Description
AllowedHosts Limite les hôtes par l’en-tête X-Forwarded-Host aux valeurs fournies.
  • Les valeurs sont comparées à l’aide de l’option ordinal-ignore-case.
  • Les numéros de port doivent être exclus.
  • Si la liste est vide, tous les hôtes sont autorisés.
  • Un caractère générique de niveau supérieur * autorise tous les hôtes non vides.
  • Les caractères génériques de sous-domaine sont autorisés, mais ne correspondent pas au domaine racine. Par exemple, *.contoso.com correspond au sous-domaine foo.contoso.com, mais pas au domaine racine contoso.com.
  • Les noms d’hôte Unicode sont autorisés, mais sont convertis en Punycode à des fins de correspondance.
  • Les adresses IPv6 doivent inclure des crochets de délimitation et adopter une forme conventionnelle (par exemple, [ABCD:EF01:2345:6789:ABCD:EF01:2345:6789]). Les adresses IPv6 ne sont pas les seules à rechercher une égalité logique entre différents formats, et aucune canonicalisation n’est effectuée.
  • La non-restriction des hôtes autorisés peut permettre à un attaquant d’usurper les liens générés par le service.
La valeur par défaut est un IList<string> vide.
ForwardedForHeaderName Utilisez l’en-tête spécifié par cette propriété à la place de celui spécifié par ForwardedHeadersDefaults.XForwardedForHeaderName. Cette option est utilisée quand le proxy/redirecteur utilise un en-tête autre que l’en-tête X-Forwarded-For pour transférer les informations.

Par défaut, il s’agit de X-Forwarded-For.
ForwardedHeaders Identifie les redirecteurs à traiter. Consultez l’énumération ForwardedHeaders pour obtenir la liste des champs qui s’appliquent. En règle générale, les valeurs attribuées à cette propriété sont ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto.

La valeur par défaut est ForwardedHeaders.None.
ForwardedHostHeaderName Utilisez l’en-tête spécifié par cette propriété à la place de celui spécifié par ForwardedHeadersDefaults.XForwardedHostHeaderName. Cette option est utilisée quand le proxy/redirecteur utilise un en-tête autre que l’en-tête X-Forwarded-Host pour transférer les informations.

Par défaut, il s’agit de X-Forwarded-Host.
ForwardedProtoHeaderName Utilisez l’en-tête spécifié par cette propriété à la place de celui spécifié par ForwardedHeadersDefaults.XForwardedProtoHeaderName. Cette option est utilisée quand le proxy/redirecteur utilise un en-tête autre que l’en-tête X-Forwarded-Proto pour transférer les informations.

Par défaut, il s’agit de X-Forwarded-Proto.
ForwardLimit Limite le nombre d’entrées dans les en-têtes qui sont traités. Définissez cette option sur null pour désactiver la limite, mais seulement si KnownProxies ou KnownNetworks sont configurés. Définir une valeur non null est une précaution (mais pas une garantie) pour vous prémunir contre les proxys mal configurés et les requêtes malveillantes provenant de canaux latéraux sur le réseau.

Le middleware des en-têtes transférés traite les en-têtes dans l’ordre inverse de droite à gauche. Si la valeur par défaut (1) est utilisée, seule la valeur la plus à droite dans les en-têtes est traitée, sauf si la valeur de ForwardLimit est augmentée.

Par défaut, il s’agit de 1.
KnownNetworks Plages d’adresses des réseaux connus à partir desquels les en-têtes transférés peuvent être acceptés. Indiquez les plages d’adresses IP à l’aide de la notation CIDR (Classless Interdomain Routing).

Si le serveur utilise des sockets en mode double, les adresses IPv4 sont fournies dans un format IPv6 (par exemple, 10.0.0.1 dans IPv4 représentée dans IPv6 en tant que ::ffff:10.0.0.1). Consultez IPAddress.MapToIPv6. Déterminez si ce format est nécessaire en examinant HttpContext.Connection.RemoteIpAddress.

La valeur par défaut est une IList><IPNetworkentrée unique pour .IPAddress.Loopback
KnownProxies Adresses des proxy connus à partir desquels les en-têtes transférés peuvent être acceptés. Utilisez KnownProxies pour spécifier des correspondances d’adresse IP exactes.

Si le serveur utilise des sockets en mode double, les adresses IPv4 sont fournies dans un format IPv6 (par exemple, 10.0.0.1 dans IPv4 représentée dans IPv6 en tant que ::ffff:10.0.0.1). Consultez IPAddress.MapToIPv6. Déterminez si ce format est nécessaire en examinant HttpContext.Connection.RemoteIpAddress.

La valeur par défaut est une IList><IPAddressentrée unique pour .IPAddress.IPv6Loopback
OriginalForHeaderName Utilisez l’en-tête spécifié par cette propriété à la place de celui spécifié par ForwardedHeadersDefaults.XOriginalForHeaderName.

Par défaut, il s’agit de X-Original-For.
OriginalHostHeaderName Utilisez l’en-tête spécifié par cette propriété à la place de celui spécifié par ForwardedHeadersDefaults.XOriginalHostHeaderName.

Par défaut, il s’agit de X-Original-Host.
OriginalProtoHeaderName Utilisez l’en-tête spécifié par cette propriété à la place de celui spécifié par ForwardedHeadersDefaults.XOriginalProtoHeaderName.

Par défaut, il s’agit de X-Original-Proto.
RequireHeaderSymmetry Cette option impose que le nombre de valeurs d’en-tête soit synchronisé avec les ForwardedHeadersOptions.ForwardedHeaders en cours de traitement.

La valeur par défaut dans ASP.NET Core 1.x est true. La valeur par défaut dans ASP.NET Core versions 2.0 ou ultérieures est false.

Scénarios et cas d’usage

Quand il est impossible d’ajouter des en-têtes transférés et que toutes les requêtes sont sécurisées

Dans certains cas, il peut être impossible d’ajouter des en-têtes transférés aux requêtes qui sont transmises par proxy à l’application. Si le proxy applique que toutes les requêtes externes publiques sont HTTPS, le schéma peut être défini manuellement avant d’utiliser n’importe quel type d’intergiciel :

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

Ce code peut être désactivé avec une variable d’environnement ou un autre paramètre de configuration dans un environnement de développement ou intermédiaire :

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

Utiliser la base de chemin d’accès et les proxys qui modifient le chemin d’accès de la demande

Certains proxys passent le chemin tel quel, mais avec un chemin de base d’application qui doit être supprimé afin que le routage fonctionne correctement. Le middleware UsePathBaseExtensions.UsePathBase fractionne le chemin en HttpRequest.Path et le chemin de base d’application en HttpRequest.PathBase.

Si /foo est le chemin de base d’application pour un chemin de proxy passé en tant que /foo/api/1, le middleware définit Request.PathBase sur /foo et Request.Path sur /api/1 avec la commande suivante :

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

Notes

Lors de l’utilisation de WebApplication (consultez Migrer de ASP.NET Core 5.0 vers 6.0), app.UseRouting doit être appelé après UsePathBase afin que le middleware de routage puisse observer le chemin modifié avant la mise en correspondance des routes. Dans le cas contraire, les routes sont mises en correspondance avant que le chemin ne soit réécrit par UsePathBase comme décrit dans les articles Ordre des intergiciels et Routage.

Le chemin et la base du chemin d’origine sont réappliqués quand le middleware est rappelé dans l’ordre inverse. Pour plus d’informations sur le traitement des commandes d’intergiciels, consultez ASP.NET Core middleware.

Si le proxy supprime le chemin (par exemple, en transférant /foo/api/1 vers /api/1), corrigez les redirections et les liens en définissant la propriété PathBase de la requête :

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

Si le proxy ajoute des données de chemin, abandonnez la partie du chemin pour corriger les redirections et les liens en utilisant StartsWithSegments et en l’attribuant à la propriété Path :

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

    return next(context);
});

Configuration d’un proxy qui utilise des noms d’en-têtes différents

Si le proxy n’utilise pas d’en-têtes nommés X-Forwarded-For et X-Forwarded-Proto pour transférer l’adresse/le port du proxy ainsi que les informations du schéma d’origine, définissez les options ForwardedForHeaderName et ForwardedProtoHeaderName pour faire correspondre les noms d’en-têtes utilisés par le proxy :

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

Transférer le schéma pour les serveurs proxy inverses Linux et non IIS

Les applications qui appellent UseHttpsRedirection et UseHsts permettent de placer un site dans une boucle infinie, s’il est déployé sur Azure Linux App Service, une machine virtuelle Azure Linux ou derrière tout autre proxy inverse en dehors d’IIS. TLS est arrêté par le proxy inverse et Kestrel n’est pas informé du schéma de requête correct. OAuth et OIDC sont également défaillants dans cette configuration, car ils génèrent des redirections incorrectes. UseIISIntegration ajoute et configure le middleware (intergiciel) des en-têtes transférés durant l’exécution avec IIS, mais il n’existe aucune configuration automatique correspondante pour Linux (intégration d’Apache ou de Nginx).

Pour transférer le schéma à partir du proxy dans les scénarios non IIS, activez l’intergiciel des en-têtes transférés en définissant ASPNETCORE_FORWARDEDHEADERS_ENABLEDtruesur . Avertissement : cet indicateur utilise les paramètres conçus pour les environnements cloud et n’active pas les fonctionnalités telles que la KnownProxies option restriction des redirecteurs d’adresses IP acceptées.

Transfert de certificat

Azure

Pour configurer Azure App Service pour le transfert de certificats, consultez Configurer l’authentification mutuelle TLS pour Azure App Service. Les instructions suivantes concernent la configuration de l’application 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();

Autres proxys web

Si un proxy est utilisé qui n’est pas IIS ou si l’arr (Application Request Routing) d’Azure App Service n’est pas utilisé, configurez le proxy pour transférer le certificat reçu dans un en-tête 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();

Si le proxy n’est pas codé en base64, comme c’est le cas avec Nginx, définissez l’option HeaderConverter . Prenons l’exemple suivant :

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

Dépanner

Lorsque les en-têtes ne sont pas transférés comme prévu, activez debug la journalisation au niveau et la journalisation des requêtes HTTP. UseHttpLogging doit être appelé après 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();

Lors du traitement, les valeurs X-Forwarded-{For|Proto|Host} sont déplacées vers X-Original-{For|Proto|Host}. S’il existe plusieurs valeurs dans un en-tête donné, le middleware des en-têtes transférés traite les en-têtes dans l’ordre inverse de droite à gauche. La valeur par défaut de ForwardLimit est 1 si bien que seule la valeur la plus à droite dans les en-têtes est traitée, sauf si la valeur de ForwardLimit est augmentée.

L’adresse IP distante d’origine de la requête doit correspondre à une entrée dans les listes KnownProxies ou KnownNetworks pour que les en-têtes transférés soient traités. L’usurpation des en-têtes s’en trouve limitée, car les redirecteurs émanant de proxys non approuvés ne sont pas acceptés. Lorsqu’un proxy inconnu est détecté, la journalisation indique l’adresse du proxy :

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

Dans l’exemple précédent, 10.0.0.100 est un serveur proxy. Si le serveur est un proxy approuvé, ajoutez l’adresse IP du serveur à KnownProxies, ou ajoutez un réseau approuvé à KnownNetworks. Pour plus d’informations, consultez la section Options du middleware des en-têtes transférés.

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

Pour afficher les journaux d’activité, ajoutez-y "Microsoft.AspNetCore.HttpLogging": "Information" :appsettings.Development.json

{
  "DetailedErrors": true,
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.AspNetCore.HttpLogging": "Information"
    }
  }
}

Important

Autorisez uniquement des réseaux et des proxies approuvés pour transférer des en-têtes. Sinon, des attaques d’usurpation d’adresse IP sont possibles.

Ressources supplémentaires

Dans la configuration recommandée d’ASP.NET Core, l’application est hébergée à l’aide du module IIS/ASP.NET Core, de Nginx ou d’Apache. Les serveurs proxy, les équilibreurs de charge et autres dispositifs réseau masquent souvent les informations sur la requête avant qu’elle n’atteigne l’application :

  • Quand les requêtes HTTPS sont transmises par proxy via HTTP, le schéma d’origine (HTTPS) est perdu et doit être transféré dans un en-tête.
  • Étant donné qu’une requête adressée à une application transite par le proxy au lieu de provenir directement de sa source sur Internet ou le réseau d’entreprise, l’adresse IP cliente d’origine doit également être transférée dans un en-tête.

Ces informations peuvent être importantes pour traiter les requêtes, par exemple dans les redirections, l’authentification, la génération de lien, l’évaluation des stratégies et la géolocalisation des clients.

En-têtes transférés

Par convention, les proxys transfèrent les informations dans des en-têtes HTTP.

En-tête Description
X-Forwarded-For Contient des informations sur le client à l’origine de la requête et les proxys suivants dans une chaîne de proxys. Ce paramètre peut contenir des adresses IP (et, éventuellement, des numéros de port). Dans une chaîne de serveurs proxy, le premier paramètre indique le client sur lequel la requête a été effectuée pour la première fois. Viennent ensuite les identificateurs des proxy suivants. Le dernier proxy dans la chaîne ne figure pas dans la liste des paramètres. L’adresse IP du dernier proxy et éventuellement un numéro de port sont disponibles en tant qu’adresse IP distante au niveau de la couche transport.
X-Forwarded-Proto Valeur du schéma d’origine (HTTP/HTTPS). La valeur peut également être une liste de schémas si la requête a franchi plusieurs proxys.
X-Forwarded-Host Valeur d’origine du champ d’en-tête de l’hôte. En règle générale, les proxys ne modifient pas l’en-tête de l’hôte. Consultez le document Microsoft Security Advisory CVE-2018-0787 pour plus d’informations sur une vulnérabilité par élévation de privilèges qui affecte les systèmes où le proxy ne valide pas les en-têtes d’hôte ou ne les limite pas à des valeurs correctes connues.

L’intergiciel des en-têtes transférés (ForwardedHeadersMiddleware), lit ces en-têtes et remplit les champs associés sur HttpContext.

Le middleware met à jour les éléments suivants :

Pour plus d’informations sur le problème précédent, consultez ce problème GitHub.

Vous pouvez configurer les paramètres par défaut du middleware des en-têtes transférés. Pour les paramètres par défaut :

  • Il y a un seul proxy entre l’application et la source des requêtes.
  • Seules des adresses de bouclage sont configurées pour les proxys connus et les réseaux connus.
  • Les en-têtes transférés sont nommés X-Forwarded-For et X-Forwarded-Proto.
  • La ForwardedHeaders valeur est ForwardedHeaders.None, les redirecteurs souhaités doivent être définis ici pour activer le middleware.

Certaines appliances réseau nécessitent une configuration supplémentaire pour ajouter les en-têtes X-Forwarded-For et X-Forwarded-Proto. Consultez les instructions du fabricant de votre appliance si des requêtes en proxy ne contiennent pas ces en-têtes quand elles atteignent l’application. Si l’appliance utilise des noms d’en-têtes autres que X-Forwarded-For et X-Forwarded-Proto, définissez les options ForwardedForHeaderName et ForwardedProtoHeaderName pour faire correspondre les noms d’en-têtes utilisés par l’appliance. Pour plus d’informations, consultez Options du middleware des en-têtes transférés et Configuration d’un proxy qui utilise des noms d’en-têtes différents.

Module ASP.NET Core et IIS/IIS Express

Le middleware des en-têtes transférés est activé par défaut par le middleware d’intégration IIS lorsque l’application est hébergée out-of-process derrière IIS et le module ASP.NET Core. Le middleware des en-têtes transférés est activé pour s’exécuter en premier dans le pipeline des middlewares avec une configuration limitée propre au module ASP.NET Core en raison de problèmes d’approbation liés aux en-têtes transférés (par exemple, usurpation d’adresse IP). Le middleware est configuré pour transférer les en-têtes X-Forwarded-For et X-Forwarded-Proto et est limité à un proxy localhost unique. Si une configuration supplémentaire est requise, consultez les options du middleware des en-têtes transférés.

Autres scénarios avec un serveur proxy et un équilibreur de charge

En dehors de l’utilisation de l’intégration IIS lors de l’hébergement out-of-process, le middleware des en-têtes transférés n’est pas activé par défaut. L’intergiciel des en-têtes transférés doit être activé pour qu’une application puisse traiter les en-têtes transférés avec UseForwardedHeaders. Une fois l’intergiciel activé, si aucune option ForwardedHeadersOptions ne lui est spécifiée, les valeurs par défaut ForwardedHeadersOptions.ForwardedHeaders ont pour valeur ForwardedHeaders.None.

Configurez l’intergiciel avec ForwardedHeadersOptions pour transférer les en-têtes X-Forwarded-For et X-Forwarded-Proto dans Startup.ConfigureServices.

Ordre du middleware Forwarded Headers

Le middleware Forwarded Headers doit s’exécuter avant tout autre middleware. Cet ordre permet au middleware qui repose sur les informations des en-têtes transférés d’utiliser les valeurs d’en-tête pour le traitement. Le middleware des en-têtes transférés peut s’exécuter après la gestion des diagnostics et des erreurs, mais il doit être exécuté avant d’appeler 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?}");
        });
    }
}

Vous pouvez également appeler UseForwardedHeaders avant les diagnostics :

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?}");
    });
}

Notes

Si aucun ForwardedHeadersOptions n’est spécifié dans Startup.ConfigureServices ou directement sur la méthode d’extension avec UseForwardedHeaders, les en-têtes par défaut à transférer sont ForwardedHeaders.None. La propriété ForwardedHeaders doit être configurée avec les en-têtes à transférer.

Configuration de Nginx

Pour transférer les en-têtes et X-Forwarded-Proto les X-Forwarded-For en-têtes, consultez host ASP.NET Core sur Linux avec Nginx. Pour plus d’informations, consultez NGINX: Using the Forwarded header (NGINX : utilisation de l’en-tête Forwarded).

Configuration d’Apache

X-Forwarded-For est ajouté automatiquement (consultez Module Apache mod_proxy : en-têtes de requête du mandataire inverse). Pour plus d’informations sur la façon de transférer l’en-têteX-Forwarded-Proto, consultez Host ASP.NET Core sur Linux avec Apache.

Options du middleware des en-têtes transférés

ForwardedHeadersOptions contrôlent le comportement de l’intergiciel des en-têtes transférés. L’exemple suivant change les valeurs par défaut :

  • Limitez le nombre d’entrées dans les en-têtes transférés à 2.
  • Ajoutez l’adresse de proxy connue 127.0.10.1.
  • Changez le nom de l’en-tête transféré en remplaçant la valeur par défaut X-Forwarded-For par 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";
});
Option Description
AllowedHosts Limite les hôtes par l’en-tête X-Forwarded-Host aux valeurs fournies.
  • Les valeurs sont comparées à l’aide de l’option ordinal-ignore-case.
  • Les numéros de port doivent être exclus.
  • Si la liste est vide, tous les hôtes sont autorisés.
  • Un caractère générique de niveau supérieur * autorise tous les hôtes non vides.
  • Les caractères génériques de sous-domaine sont autorisés, mais ne correspondent pas au domaine racine. Par exemple, *.contoso.com correspond au sous-domaine foo.contoso.com, mais pas au domaine racine contoso.com.
  • Les noms d’hôte Unicode sont autorisés, mais sont convertis en Punycode à des fins de correspondance.
  • Les adresses IPv6 doivent inclure des crochets de délimitation et adopter une forme conventionnelle (par exemple, [ABCD:EF01:2345:6789:ABCD:EF01:2345:6789]). Les adresses IPv6 ne sont pas les seules à rechercher une égalité logique entre différents formats, et aucune canonicalisation n’est effectuée.
  • La non-restriction des hôtes autorisés peut permettre à un attaquant d’usurper les liens générés par le service.
La valeur par défaut est un IList<string> vide.
ForwardedForHeaderName Utilisez l’en-tête spécifié par cette propriété à la place de celui spécifié par ForwardedHeadersDefaults.XForwardedForHeaderName. Cette option est utilisée quand le proxy/redirecteur utilise un en-tête autre que l’en-tête X-Forwarded-For pour transférer les informations.

Par défaut, il s’agit de X-Forwarded-For.
ForwardedHeaders Identifie les redirecteurs à traiter. Consultez l’énumération ForwardedHeaders pour obtenir la liste des champs qui s’appliquent. En règle générale, les valeurs attribuées à cette propriété sont ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto.

La valeur par défaut est ForwardedHeaders.None.
ForwardedHostHeaderName Utilisez l’en-tête spécifié par cette propriété à la place de celui spécifié par ForwardedHeadersDefaults.XForwardedHostHeaderName. Cette option est utilisée quand le proxy/redirecteur utilise un en-tête autre que l’en-tête X-Forwarded-Host pour transférer les informations.

Par défaut, il s’agit de X-Forwarded-Host.
ForwardedProtoHeaderName Utilisez l’en-tête spécifié par cette propriété à la place de celui spécifié par ForwardedHeadersDefaults.XForwardedProtoHeaderName. Cette option est utilisée quand le proxy/redirecteur utilise un en-tête autre que l’en-tête X-Forwarded-Proto pour transférer les informations.

Par défaut, il s’agit de X-Forwarded-Proto.
ForwardLimit Limite le nombre d’entrées dans les en-têtes qui sont traités. Définissez cette option sur null pour désactiver la limite, mais seulement si KnownProxies ou KnownNetworks sont configurés. Définir une valeur non null est une précaution (mais pas une garantie) pour vous prémunir contre les proxys mal configurés et les requêtes malveillantes provenant de canaux latéraux sur le réseau.

Le middleware des en-têtes transférés traite les en-têtes dans l’ordre inverse de droite à gauche. Si la valeur par défaut (1) est utilisée, seule la valeur la plus à droite dans les en-têtes est traitée, sauf si la valeur de ForwardLimit est augmentée.

Par défaut, il s’agit de 1.
KnownNetworks Plages d’adresses des réseaux connus à partir desquels les en-têtes transférés peuvent être acceptés. Indiquez les plages d’adresses IP à l’aide de la notation CIDR (Classless Interdomain Routing).

Si le serveur utilise des sockets en mode double, les adresses IPv4 sont fournies dans un format IPv6 (par exemple, 10.0.0.1 dans IPv4 représentée dans IPv6 en tant que ::ffff:10.0.0.1). Consultez IPAddress.MapToIPv6. Déterminez si ce format est nécessaire en examinant HttpContext.Connection.RemoteIpAddress.

La valeur par défaut est une IList><IPNetworkentrée unique pour .IPAddress.Loopback
KnownProxies Adresses des proxy connus à partir desquels les en-têtes transférés peuvent être acceptés. Utilisez KnownProxies pour spécifier des correspondances d’adresse IP exactes.

Si le serveur utilise des sockets en mode double, les adresses IPv4 sont fournies dans un format IPv6 (par exemple, 10.0.0.1 dans IPv4 représentée dans IPv6 en tant que ::ffff:10.0.0.1). Consultez IPAddress.MapToIPv6. Déterminez si ce format est nécessaire en examinant HttpContext.Connection.RemoteIpAddress.

La valeur par défaut est une IList><IPAddressentrée unique pour .IPAddress.IPv6Loopback
OriginalForHeaderName Utilisez l’en-tête spécifié par cette propriété à la place de celui spécifié par ForwardedHeadersDefaults.XOriginalForHeaderName.

Par défaut, il s’agit de X-Original-For.
OriginalHostHeaderName Utilisez l’en-tête spécifié par cette propriété à la place de celui spécifié par ForwardedHeadersDefaults.XOriginalHostHeaderName.

Par défaut, il s’agit de X-Original-Host.
OriginalProtoHeaderName Utilisez l’en-tête spécifié par cette propriété à la place de celui spécifié par ForwardedHeadersDefaults.XOriginalProtoHeaderName.

Par défaut, il s’agit de X-Original-Proto.
RequireHeaderSymmetry Cette option impose que le nombre de valeurs d’en-tête soit synchronisé avec les ForwardedHeadersOptions.ForwardedHeaders en cours de traitement.

La valeur par défaut dans ASP.NET Core 1.x est true. La valeur par défaut dans ASP.NET Core versions 2.0 ou ultérieures est false.

Scénarios et cas d’usage

Quand il est impossible d’ajouter des en-têtes transférés et que toutes les requêtes sont sécurisées

Dans certains cas, il peut être impossible d’ajouter des en-têtes transférés aux requêtes qui sont transmises par proxy à l’application. Si le proxy impose que toutes les requêtes externes publiques soient de type HTTPS, vous pouvez définir le schéma manuellement dans Startup.Configure avant d’utiliser tout type de middleware :

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

Vous pouvez désactiver ce code avec une variable d’environnement ou un autre paramètre de configuration dans un environnement de préproduction ou de développement.

Traiter la base du chemin et les proxys qui changent le chemin de la requête

Certains proxys passent le chemin tel quel, mais avec un chemin de base d’application qui doit être supprimé afin que le routage fonctionne correctement. Le middleware UsePathBaseExtensions.UsePathBase fractionne le chemin en HttpRequest.Path et le chemin de base d’application en HttpRequest.PathBase.

Si /foo est le chemin de base d’application pour un chemin de proxy passé en tant que /foo/api/1, le middleware définit Request.PathBase sur /foo et Request.Path sur /api/1 avec la commande suivante :

app.UsePathBase("/foo");

Le chemin et la base du chemin d’origine sont réappliqués quand le middleware est rappelé dans l’ordre inverse. Pour plus d’informations sur le traitement des commandes d’intergiciels, consultez ASP.NET Core middleware.

Si le proxy supprime le chemin (par exemple, en transférant /foo/api/1 vers /api/1), corrigez les redirections et les liens en définissant la propriété PathBase de la requête :

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

Si le proxy ajoute des données de chemin, abandonnez la partie du chemin pour corriger les redirections et les liens en utilisant StartsWithSegments et en l’attribuant à la propriété Path :

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

    return next();
});

Configuration d’un proxy qui utilise des noms d’en-têtes différents

Si le proxy n’utilise pas d’en-têtes nommés X-Forwarded-For et X-Forwarded-Proto pour transférer l’adresse/le port du proxy ainsi que les informations du schéma d’origine, définissez les options ForwardedForHeaderName et ForwardedProtoHeaderName pour faire correspondre les noms d’en-têtes utilisés par le proxy :

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

Transférer le schéma pour les serveurs proxy inverses Linux et non IIS

Les applications qui appellent UseHttpsRedirection et UseHsts permettent de placer un site dans une boucle infinie, s’il est déployé sur Azure Linux App Service, une machine virtuelle Azure Linux ou derrière tout autre proxy inverse en dehors d’IIS. TLS est arrêté par le proxy inverse et Kestrel n’est pas informé du schéma de requête correct. OAuth et OIDC sont également défaillants dans cette configuration, car ils génèrent des redirections incorrectes. UseIISIntegration ajoute et configure le middleware (intergiciel) des en-têtes transférés durant l’exécution avec IIS, mais il n’existe aucune configuration automatique correspondante pour Linux (intégration d’Apache ou de Nginx).

Pour transférer le schéma à partir du proxy dans les scénarios non basés sur IIS, ajoutez et configurez le middleware des en-têtes transférés. Dans Startup.ConfigureServices, utilisez le code suivant :

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

Transfert de certificat

Azure

Pour configurer Azure App Service pour le transfert de certificats, consultez Configurer l’authentification mutuelle TLS pour Azure App Service. Les instructions suivantes concernent la configuration de l’application ASP.NET Core.

Dans Startup.Configure, ajoutez le code suivant avant l’appel à app.UseAuthentication();:

app.UseCertificateForwarding();

Configurez le middleware de transfert de certificat pour spécifier le nom d’en-tête qu’Azure utilise. Dans Startup.ConfigureServices, ajoutez le code suivant pour configurer l’en-tête à partir duquel l’intergiciel génère un certificat :

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

Autres proxys web

Si un proxy est utilisé qui n’est pas IIS ou si l’arr (Application Request Routing) d’Azure App Service n’est pas utilisé, configurez le proxy pour transférer le certificat reçu dans un en-tête HTTP. Dans Startup.Configure, ajoutez le code suivant avant l’appel à app.UseAuthentication();:

app.UseCertificateForwarding();

Configurez le middleware de transfert de certificat pour spécifier le nom de l’en-tête. Dans Startup.ConfigureServices, ajoutez le code suivant pour configurer l’en-tête à partir duquel l’intergiciel génère un certificat :

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

Si le proxy n’est pas codé en base64, le certificat (comme c’est le cas avec Nginx), définissez l’option HeaderConverter . Prenons l’exemple suivant dans 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;
    }
});

Dépanner

Quand des en-têtes ne sont pas transférés comme prévu, activez la journalisation. Si les journaux ne fournissent pas suffisamment d’informations pour résoudre le problème, énumérez les en-têtes de requête reçus par le serveur. Utilisez le middleware inclus pour écrire les en-têtes de requête dans une réponse d’application, ou consignez les en-têtes dans le fichier journal.

Pour écrire les en-têtes dans la réponse de l’application, placez le middleware inline de terminal suivant juste après l’appel à UseForwardedHeaders dans 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}");
});

Il est possible d’écrire dans les journaux plutôt que dans le corps de la réponse ; ainsi, le site fonctionnera normalement pendant le débogage.

Pour écrire dans les journaux plutôt que dans le corps de la réponse :

app.Use(async (context, next) =>
{
    // Request method, scheme, and 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);

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

Lors du traitement, les valeurs X-Forwarded-{For|Proto|Host} sont déplacées vers X-Original-{For|Proto|Host}. S’il existe plusieurs valeurs dans un en-tête donné, le middleware des en-têtes transférés traite les en-têtes dans l’ordre inverse de droite à gauche. La valeur par défaut de ForwardLimit est 1 si bien que seule la valeur la plus à droite dans les en-têtes est traitée, sauf si la valeur de ForwardLimit est augmentée.

L’adresse IP distante d’origine de la requête doit correspondre à une entrée dans les listes KnownProxies ou KnownNetworks pour que les en-têtes transférés soient traités. L’usurpation des en-têtes s’en trouve limitée, car les redirecteurs émanant de proxys non approuvés ne sont pas acceptés. Lorsqu’un proxy inconnu est détecté, la journalisation indique l’adresse du proxy :

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

Dans l’exemple précédent, 10.0.0.100 est un serveur proxy. Si le serveur est un proxy approuvé, ajoutez l’adresse IP du serveur à la liste KnownProxies (ou ajoutez un réseau approuvé à la liste KnownNetworks) dans Startup.ConfigureServices. Pour plus d’informations, consultez la section Options du middleware des en-têtes transférés.

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

Important

Autorisez uniquement des réseaux et des proxies approuvés pour transférer des en-têtes. Sinon, des attaques d’usurpation d’adresse IP sont possibles.

Ressources supplémentaires