Partager via


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

Remarque

Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 9 de cet article.

Avertissement

Cette version d’ASP.NET Core n’est plus prise en charge. Pour plus d’informations, consultez la stratégie de support .NET et .NET Core. Pour la version actuelle, consultez la version .NET 9 de cet article.

Important

Ces informations portent sur la préversion du produit, qui est susceptible d’être en grande partie modifié avant sa commercialisation. Microsoft n’offre aucune garantie, expresse ou implicite, concernant les informations fournies ici.

Pour la version actuelle, consultez la version .NET 9 de cet article.

Par Chris Ross

Dans la configuration recommandée d’ASP.NET Core, l’application est hébergée à l’aide du module ASP.NET Core (ANCM) pour IIS, 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.

Les applications destinées à s’exécuter sur la batterie de serveurs web doivent lire Host ASP.NET Core dans une batterie de serveurs web.

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.
X-Forwarded-Prefix Chemin d’accès de base d’origine demandé par le client. Cet en-tête peut être utile pour que les applications génèrent correctement des URL, des redirections ou des liens pour revenir vers le client.

L’intergiciel d’en-têtes transférés, ForwardedHeadersMiddleware, lit ces en-têtes et renseigne les champs associés sur HttpContext.

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

Pour plus d’informations sur ce qui précède, 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, X-Forwarded-Proto, X-Forwarded-Host et X-Forwarded-Prefix.
  • La valeur ForwardedHeaders est ForwardedHeaders.None, les redirecteurs souhaités doivent être définis ici pour activer l’intergiciel.

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

L’intergiciel d’en-têtes transférés est activé par défaut par l’intergiciel d’intégration IIS lorsque l’application est hébergée out-of-process derrière IIS et le module ASP.NET Core (ANCM) pour IIS. L’intergiciel d’en-têtes transférés est activé pour s’exécuter en premier dans le pipeline des intergiciels avec une configuration limitée propre au module ASP.NET Core. La configuration restreinte est due à des problèmes d’approbation liés aux 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 out-of-process, l’intergiciel d’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.

Ordre du middleware Forwarded Headers

L’intergiciel d’en-têtes transférés doit s’exécuter avant tout autre intergiciel. 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. L’intergiciel d’en-têtes transférés peut s’exécuter après les diagnostics et la gestion 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();

Remarque

Si aucun ForwardedHeadersOptions n’est spécifié ou appliqué 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 X-Forwarded-For et X-Forwarded-Proto, consultez Héberger 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 Module Apache mod_proxy : en-têtes de requête de proxy inverse.

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

Les ForwardedHeadersOptions contrôlent le comportement de l’intergiciel d’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 l’adresse de proxy connue 127.0.10.1.
  • Change 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.
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 cyberattaquant 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 auxiliaires 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<IPNetwork> contenant une seule entrée pour new IPNetwork(IPAddress.Loopback, 8).
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<IPAddress> contenant une seule entrée 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 avant d’utiliser tout 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();

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 :

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 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");
// ...
app.UseRouting();

Remarque

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 l’ordre de traitement des intergiciels, consultez Intergiciels ASP.NET Core.

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. Le proxy inverse met fin à TLS, et Kestrel n’a pas connaissance du schéma de requête approprié. 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 des scénarios non IIS, activez l’intergiciel d’en-têtes transférés en définissant ASPNETCORE_FORWARDEDHEADERS_ENABLED sur true. Avertissement : cet indicateur utilise des paramètres conçus pour les environnements cloud, et ne permet pas à des fonctionnalités comme KnownProxies option de restreindre les redirecteurs d’adresses IP acceptés.

Transfert de certificat

Azure

Pour configurer Azure App Service pour le transfert de certificat, consultez Configurer l’authentification mutuelle TLS pour Azure App Service. Les conseils suivants 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 qui n’est pas IIS ou ARR (Application Request Routing) d’Azure App Service est utilisé, configurez le proxy pour transférer le certificat qu’il a 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 encodé en base64, le certificat, 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();

Résoudre les problèmes

Lorsque les en-têtes ne sont pas transférés comme prévu, activez la journalisation de niveau debug 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();

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é à 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, ajoutez "Microsoft.AspNetCore.HttpLogging": "Information" au fichier 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 d’en-têtes transférés (ForwardedHeadersMiddleware) lit ces en-têtes et renseigne les champs associés sur HttpContext.

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

Pour plus d’informations sur ce qui précède, 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 valeur ForwardedHeaders est ForwardedHeaders.None, les redirecteurs souhaités doivent être définis ici pour activer l’intergiciel.

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

L’intergiciel d’en-têtes transférés doit s’exécuter avant tout autre intergiciel. 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. L’intergiciel d’en-têtes transférés peut s’exécuter après les diagnostics et la gestion 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?}");
    });
}

Remarque

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 X-Forwarded-For et X-Forwarded-Proto, consultez Héberger 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).

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 cyberattaquant d’usurper les liens générés par le service.
La valeur par défaut est un IList<string> vide.
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.
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.
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.
ForwardedPrefixHeaderName Utilisez l’en-tête spécifié par cette propriété à la place de celui spécifié par ForwardedHeadersDefaults.XForwardedPrefixHeaderName. Cette option est utilisée quand le proxy/redirecteur utilise un en-tête autre que l’en-tête X-Forwarded-Prefix pour transférer les informations.

Par défaut, il s’agit de X-Forwarded-Prefix.
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 auxiliaires 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<IPNetwork> contenant une seule entrée pour new IPNetwork(IPAddress.Loopback, 8).
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<IPAddress> contenant une seule entrée 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.
OriginalPrefixHeaderName Utilisez l’en-tête spécifié par cette propriété à la place de celui spécifié par ForwardedHeadersDefaults.XOriginalPrefixHeaderName.

Par défaut, il s’agit de X-Original-Prefix.
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 l’ordre de traitement des intergiciels, consultez Intergiciels ASP.NET Core.

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. Le proxy inverse met fin à TLS, et Kestrel n’a pas connaissance du schéma de requête approprié. 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 certificat, consultez Configurer l’authentification mutuelle TLS pour Azure App Service. Les conseils suivants concernent la configuration de l’application ASP.NET Core.

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

app.UseCertificateForwarding();

Configurez l’intergiciel 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 qui n’est pas IIS ou ARR (Application Request Routing) d’Azure App Service est utilisé, configurez le proxy pour transférer le certificat qu’il a reçu dans un en-tête HTTP. Dans Startup.Configure, ajoutez le code suivant avant l’appel à app.UseAuthentication(); :

app.UseCertificateForwarding();

Configurez l’intergiciel de transfert de certificat pour spécifier le nom d’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 encodé 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;
    }
});

Résoudre les problèmes

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

Lors du traitement, les valeurs X-Forwarded-{For|Proto|Host|Prefix} sont déplacées vers X-Original-{For|Proto|Host|Prefix}. 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