Activation des demandes multi-origines (CORS) dans ASP.NET Core

Notes

Il ne s’agit pas de la dernière version de cet article. Pour basculer vers la dernière version, utilisez le sélecteur de version ASP.NET Core en haut de la table des matières.
Sélecteur de version
Si le sélecteur n’est pas visible dans une fenêtre de navigateur étroite, élargissez la fenêtre ou sélectionnez les points de suspension verticaux (⋮) >Table des matières.
Sélecteur de table des matières

Par Rick Anderson et Kirk Larkin

Cet article explique comment activer CORS dans une application ASP.NET Core.

La sécurité du navigateur empêche une page web d’envoyer des requêtes à un domaine différent de celui qui a servi la page web. Cette restriction est appelée stratégie de même origine. La stratégie de même origine empêche un site malveillant de lire des données sensibles à partir d’un autre site. Parfois, vous pouvez autoriser d’autres sites à effectuer des demandes d’origine croisée pour votre application. Pour plus d’informations, consultez l’article Mozilla CORS.

Partage des ressources d’origine croisée (CORS) :

  • Est une norme W3C qui permet à un serveur d’assouplir la stratégie de même origine.
  • N’est pas une fonctionnalité de sécurité, CORS assouplit la sécurité. Une API n’est pas plus sûre en autorisant CORS. Pour plus d’informations, consultez Fonctionnement de CORS.
  • Permet à un serveur d’autoriser explicitement certaines demandes d’origine croisée tout en rejetant d’autres.
  • Est plus sûr et plus flexible que les techniques antérieures, comme JSONP.

Affichez ou téléchargez l’exemple de code (procédure de téléchargement)

Même origine

Deux URL ont la même origine si elles ont des schémas, des hôtes et des ports identiques (RFC 6454).

Ces deux URL ont la même origine :

  • https://example.com/foo.html
  • https://example.com/bar.html

Ces URL ont des origines différentes des deux URL précédentes :

  • https://example.net: Domaine différent
  • https://www.example.com/foo.html: Sous-domaine différent
  • http://example.com/foo.html: Schéma différent
  • https://example.com:9000/foo.html: Port différent

Activez CORS

Il existe trois façons d’activer CORS :

L’utilisation de l’attribut [EnableCors] avec une stratégie nommée fournit le meilleur contrôle pour limiter les points de terminaison qui prennent en charge CORS.

Avertissement

UseCors doit être appelé dans l’ordre correct. Pour plus d’informations, consultez Ordre des intergiciels. Par exemple, UseCors doit être appelé avant UseResponseCaching lors de l’utilisation de UseResponseCaching.

Chaque approche est détaillée dans les sections suivantes.

CORS avec une stratégie et un intergiciel nommés

CORS Middleware gère les demandes d’origine croisée. Le code suivant applique une stratégie CORS à tous les points de terminaison de l’application avec les origines spécifiées :

var  MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      policy  =>
                      {
                          policy.WithOrigins("http://example.com",
                                              "http://www.contoso.com");
                      });
});

// services.AddResponseCaching();

builder.Services.AddControllers();

var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseCors(MyAllowSpecificOrigins);

app.UseAuthorization();

app.MapControllers();

app.Run();

Le code précédent :

Avec le routage de point de terminaison, le middleware CORS doit être configuré pour s’exécuter entre les appels à UseRouting et UseEndpoints.

Consultez Test CORS pour obtenir des instructions sur le test du code similaire au code précédent.

L’appel AddCors de méthode ajoute des services CORS au conteneur de service de l’application :

var  MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      policy  =>
                      {
                          policy.WithOrigins("http://example.com",
                                              "http://www.contoso.com");
                      });
});

// services.AddResponseCaching();

builder.Services.AddControllers();

var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseCors(MyAllowSpecificOrigins);

app.UseAuthorization();

app.MapControllers();

app.Run();

Pour plus d’informations, consultez Options de stratégie CORS dans ce document.

Les CorsPolicyBuilder méthodes peuvent être chaînées, comme indiqué dans le code suivant :

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(MyAllowSpecificOrigins,
                          policy =>
                          {
                              policy.WithOrigins("http://example.com",
                                                  "http://www.contoso.com")
                                                  .AllowAnyHeader()
                                                  .AllowAnyMethod();
                          });
});

builder.Services.AddControllers();

var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseCors(MyAllowSpecificOrigins);

app.UseAuthorization();

app.MapControllers();

app.Run();

Remarque : L’URL spécifiée ne doit pas contenir de barre oblique de fin (/). Si l’URL se termine par /, la comparaison retourne false et aucun en-tête n’est retourné.

Ordre des UseCors et des UseStaticFiles

En règle générale, UseStaticFiles est appelé avant UseCors. Les applications qui utilisent JavaScript pour récupérer des fichiers statiques intersite doivent appeler UseCors avant UseStaticFiles.

CORS avec stratégie et intergiciel par défaut

Le code mis en évidence suivant active la stratégie CORS par défaut :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(
        policy =>
        {
            policy.WithOrigins("http://example.com",
                                "http://www.contoso.com");
        });
});

builder.Services.AddControllers();

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

app.MapControllers();

app.Run();

Le code précédent applique la stratégie CORS par défaut à tous les points de terminaison du contrôleur.

Activer Cors avec le routage de point de terminaison

Avec le routage de point de terminaison, CORS peut être activé par point de terminaison à l’aide de l’ensemble RequireCors de méthodes d’extension :

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      policy =>
                      {
                          policy.WithOrigins("http://example.com",
                                              "http://www.contoso.com");
                      });
});

builder.Services.AddControllers();
builder.Services.AddRazorPages();

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapGet("/echo",
        context => context.Response.WriteAsync("echo"))
        .RequireCors(MyAllowSpecificOrigins);

    endpoints.MapControllers()
             .RequireCors(MyAllowSpecificOrigins);

    endpoints.MapGet("/echo2",
        context => context.Response.WriteAsync("echo2"));

    endpoints.MapRazorPages();
});

app.Run();

Dans le code précédent :

  • app.UseCors active le middleware CORS. Étant donné qu’aucune stratégie par défaut n’a été configurée, app.UseCors() elle seule n’active pas CORS.
  • Les /echo points de terminaison de contrôleur et autorisent les requêtes entre origines à l’aide de la stratégie spécifiée.
  • Les /echo2 points de terminaison pages et Razor n’autorisent pas les requêtes entre origines, car aucune stratégie par défaut n’a été spécifiée.

L’attribut [DisableCors] ne désactive pas CORS qui a été activé par le routage du point de terminaison avec RequireCors.

Pour obtenir des instructions sur le test du code similaire au précédent, consultez Attribut CORS avec [EnableCors] et Méthode RequireCors .

Activer CORS avec des attributs

L’activation de CORS avec l’attribut [EnableCors] et l’application d’une stratégie nommée uniquement aux points de terminaison qui nécessitent CORS fournissent le meilleur contrôle.

L’attribut [EnableCors] offre une alternative à l’application globale de CORS. L’attribut [EnableCors] active CORS pour les points de terminaison sélectionnés, plutôt que pour tous les points de terminaison :

  • [EnableCors] spécifie la stratégie par défaut.
  • [EnableCors("{Policy String}")] spécifie une stratégie nommée.

L’attribut [EnableCors] peut être appliqué à :

  • Razor Page PageModel
  • Contrôleur
  • Méthode d’action du contrôleur

Différentes stratégies peuvent être appliquées à des contrôleurs, des modèles de page ou des méthodes d’action avec l’attribut [EnableCors] . Lorsque l’attribut [EnableCors] est appliqué à un contrôleur, un modèle de page ou une méthode d’action et que CORS est activé dans l’intergiciel, les deux stratégies sont appliquées. Nous vous déconseillons de combiner des stratégies. Utilisez le [EnableCors]attribut ou middleware, pas les deux dans la même application.

Le code suivant applique une stratégie différente à chaque méthode :

[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
    // GET api/values
    [EnableCors("AnotherPolicy")]
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
        return new string[] { "green widget", "red widget" };
    }

    // GET api/values/5
    [EnableCors("Policy1")]
    [HttpGet("{id}")]
    public ActionResult<string> Get(int id)
    {
        return id switch
        {
            1 => "green widget",
            2 => "red widget",
            _ => NotFound(),
        };
    }
}

Le code suivant crée deux stratégies CORS :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("Policy1",
        policy =>
        {
            policy.WithOrigins("http://example.com",
                                "http://www.contoso.com");
        });

    options.AddPolicy("AnotherPolicy",
        policy =>
        {
            policy.WithOrigins("http://www.contoso.com")
                                .AllowAnyHeader()
                                .AllowAnyMethod();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseRouting();

app.UseCors();

app.UseAuthorization();

app.MapControllers();

app.Run();

Pour le meilleur contrôle de la limitation des requêtes CORS :

  • Utilisez [EnableCors("MyPolicy")] avec une stratégie nommée.
  • Ne définissez pas de stratégie par défaut.
  • N’utilisez pas le routage de point de terminaison.

Le code de la section suivante répond à la liste précédente.

Consultez Test CORS pour obtenir des instructions sur le test du code similaire au code précédent.

Désactiver CORS

L’attribut [DisableCors] ne désactive pas CORS qui a été activé par le routage du point de terminaison.

Le code suivant définit la stratégie CORS "MyPolicy":

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "MyPolicy",
        policy =>
        {
            policy.WithOrigins("http://example.com",
                                "http://www.contoso.com")
                    .WithMethods("PUT", "DELETE", "GET");
        });
});

builder.Services.AddControllers();
builder.Services.AddRazorPages();

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

app.UseEndpoints(endpoints => {
    endpoints.MapControllers();
    endpoints.MapRazorPages();
});

app.Run();

Le code suivant désactive CORS pour l’action GetValues2 :

[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public IActionResult Get() =>
        ControllerContext.MyDisplayRouteInfo();

    // GET api/values/5
    [HttpGet("{id}")]
    public IActionResult Get(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // PUT api/values/5
    [HttpPut("{id}")]
    public IActionResult Put(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);


    // GET: api/values/GetValues2
    [DisableCors]
    [HttpGet("{action}")]
    public IActionResult GetValues2() =>
        ControllerContext.MyDisplayRouteInfo();

}

Le code précédent :

Pour obtenir des instructions sur le test du code précédent, consultez Test CORS .

Options de stratégie CORS

Cette section décrit les différentes options qui peuvent être définies dans une stratégie CORS :

AddPolicy est appelé dans Program.cs. Pour certaines options, il peut être utile de lire d’abord la section Fonctionnement de CORS .

Définir les origines autorisées

AllowAnyOrigin: autorise les requêtes CORS de toutes les origines avec n’importe quel schéma (http ou https). AllowAnyOrigin n’est pas sécurisé, car n’importe quel site web peut effectuer des demandes cross-origin à l’application.

Notes

La spécification de AllowAnyOrigin et AllowCredentials est une configuration non sécurisée et peut entraîner une falsification de requête intersites. Le service CORS retourne une réponse CORS non valide lorsqu’une application est configurée avec les deux méthodes.

AllowAnyOrigin affecte les demandes préliminaires et l’en-tête Access-Control-Allow-Origin . Pour plus d’informations, consultez la section Demandes de contrôle préalable.

SetIsOriginAllowedToAllowWildcardSubdomains: définit la IsOriginAllowed propriété de la stratégie comme une fonction qui permet aux origines de correspondre à un domaine générique configuré lors de l’évaluation de l’autorisation de l’origine.

var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
        policy =>
        {
            policy.WithOrigins("https://*.example.com")
                .SetIsOriginAllowedToAllowWildcardSubdomains();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Définir les méthodes HTTP autorisées

AllowAnyMethod:

  • Autorise n’importe quelle méthode HTTP :
  • Affecte les demandes préliminaires et l’en-tête Access-Control-Allow-Methods . Pour plus d’informations, consultez la section Demandes de contrôle préalable.

Définir les en-têtes de requête autorisés

Pour autoriser l’envoi d’en-têtes spécifiques dans une requête CORS, appelées en-têtes de demande d’auteur, appelez WithHeaders et spécifiez les en-têtes autorisés :

using Microsoft.Net.Http.Headers;

var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
       policy =>
       {
           policy.WithOrigins("http://example.com")
                  .WithHeaders(HeaderNames.ContentType, "x-custom-header");
       });
});

builder.Services.AddControllers();

var app = builder.Build();

Pour autoriser tous les en-têtes de demande d’auteur, appelez AllowAnyHeader:

var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
        policy =>
        {
            policy.WithOrigins("https://*.example.com")
                   .AllowAnyHeader();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

AllowAnyHeader affecte les demandes préliminaires et l’en-tête Access-Control-Request-Headers . Pour plus d’informations, consultez la section Demandes de contrôle préalable.

Une stratégie d’intergiciel CORS correspond à des en-têtes spécifiques spécifiés par WithHeaders n’est possible que lorsque les en-têtes envoyés correspondent Access-Control-Request-Headers exactement aux en-têtes indiqués dans WithHeaders.

Par exemple, considérez une application configurée comme suit :

app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));

CORS Middleware refuse une demande préliminaire avec l’en-tête de requête suivant, car Content-Language (HeaderNames.ContentLanguage) n’est pas répertorié dans WithHeaders:

Access-Control-Request-Headers: Cache-Control, Content-Language

L’application retourne une réponse 200 OK , mais ne renvoie pas les en-têtes CORS. Par conséquent, le navigateur ne tente pas la requête cross-origin.

Définir les en-têtes de réponse exposés

Par défaut, le navigateur n’expose pas tous les en-têtes de réponse à l’application. Pour plus d’informations, consultez Partage de ressources cross-origin du W3C (terminologie) : en-tête de réponse simple.

Les en-têtes de réponse disponibles par défaut sont les suivants :

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

La spécification CORS appelle ces en-têtes de réponse simples. Pour rendre d’autres en-têtes disponibles pour l’application, appelez WithExposedHeaders:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MyExposeResponseHeadersPolicy",
        policy =>
        {
            policy.WithOrigins("https://*.example.com")
                   .WithExposedHeaders("x-custom-header");
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Informations d’identification dans les demandes cross-origin

Les informations d’identification nécessitent une gestion spéciale dans une demande CORS. Par défaut, le navigateur n’envoie pas d’informations d’identification avec une requête cross-origin. Les informations d’identification incluent des cookieschémas d’authentification s et HTTP. Pour envoyer des informations d’identification avec une requête cross-origin, le client doit définir XMLHttpRequest.withCredentials sur true.

En utilisant XMLHttpRequest directement :

var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;

Utilisation de jQuery :

$.ajax({
  type: 'get',
  url: 'https://www.example.com/api/test',
  xhrFields: {
    withCredentials: true
  }
});

Utilisation de l’API Fetch :

fetch('https://www.example.com/api/test', {
    credentials: 'include'
});

Le serveur doit autoriser les informations d’identification. Pour autoriser les informations d’identification cross-origin, appelez AllowCredentials:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MyMyAllowCredentialsPolicy",
        policy =>
        {
            policy.WithOrigins("http://example.com")
                   .AllowCredentials();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

La réponse HTTP inclut un Access-Control-Allow-Credentials en-tête, qui indique au navigateur que le serveur autorise les informations d’identification pour une requête cross-origin.

Si le navigateur envoie des informations d’identification mais que la réponse n’inclut pas d’en-tête valide Access-Control-Allow-Credentials , le navigateur n’expose pas la réponse à l’application et la demande cross-origin échoue.

L’autorisation des informations d’identification cross-origin est un risque pour la sécurité. Un site web d’un autre domaine peut envoyer les informations d’identification d’un utilisateur connecté à l’application pour le compte de l’utilisateur à l’insu de l’utilisateur.

La spécification CORS indique également que la définition des origines sur "*" (toutes les origines) n’est pas valide si l’en-tête Access-Control-Allow-Credentials est présent.

Demandes préliminaires

Pour certaines requêtes CORS, le navigateur envoie une requête OPTIONS supplémentaire avant d’effectuer la requête réelle. Cette requête est appelée demande préliminaire. Le navigateur peut ignorer la demande préliminaire si toutes les conditions suivantes sont remplies :

  • La méthode de requête est GET, HEAD ou POST.
  • L’application ne définit pas d’en-têtes de requête autres que Accept, Accept-Language, Content-Language, Content-Typeou Last-Event-ID.
  • L’en-tête Content-Type , s’il est défini, a l’une des valeurs suivantes :
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

La règle sur les en-têtes de requête définis pour la demande cliente s’applique aux en-têtes définis par l’application en appelant setRequestHeader sur l’objet XMLHttpRequest . La spécification CORS appelle ces en-têtes des en-têtes de demande d’auteur. La règle ne s’applique pas aux en-têtes que le navigateur peut définir, tels que User-Agent, Hostou Content-Length.

Voici un exemple de réponse similaire à la demande préliminaire effectuée à partir du bouton [Put test] dans la section Test CORS de ce document.

General:
Request URL: https://cors3.azurewebsites.net/api/values/5
Request Method: OPTIONS
Status Code: 204 No Content

Response Headers:
Access-Control-Allow-Methods: PUT,DELETE,GET
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f8...8;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Vary: Origin

Request Headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Method: PUT
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0

La requête préliminaire utilise la méthode HTTP OPTIONS . Il peut inclure les en-têtes suivants :

Si la demande préliminaire est refusée, l’application retourne une 200 OK réponse, mais ne définit pas les en-têtes CORS. Par conséquent, le navigateur ne tente pas la requête cross-origin. Pour obtenir un exemple de demande préliminaire refusée, consultez la section Test CORS de ce document.

À l’aide des outils F12, l’application console affiche une erreur similaire à l’une des suivantes, selon le navigateur :

  • Firefox : Requête cross-origin bloquée : la même stratégie d’origine interdit la lecture de la ressource distante à l’adresse https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Motif : la demande CORS n’a pas réussi). En savoir plus
  • Chromium basé sur : l’accès à l’extraction à partirhttps://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 de l’origine ahttps://cors3.azurewebsites.net été bloqué par la stratégie CORS : La réponse à la demande préliminaire ne passe pas la vérification du contrôle d’accès : aucun en-tête « Access-Control-Allow-Origin » n’est présent sur la ressource demandée. Si une réponse opaque répond à vos besoins, définissez le mode de la requête sur « no-cors » pour extraire la ressource avec CORS désactivé.

Pour autoriser des en-têtes spécifiques, appelez WithHeaders:

using Microsoft.Net.Http.Headers;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MyAllowHeadersPolicy",
        policy =>
        {
        policy.WithOrigins("http://example.com")
                   .WithHeaders(HeaderNames.ContentType, "x-custom-header");
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Pour autoriser tous les en-têtes de demande d’auteur, appelez AllowAnyHeader:

using Microsoft.Net.Http.Headers;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MyAllowAllHeadersPolicy",
        policy =>
        {
            policy.WithOrigins("https://*.example.com")
                   .AllowAnyHeader();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Les navigateurs ne sont pas cohérents dans la façon dont ils définissent Access-Control-Request-Headers. Si l’un des deux :

  • Les en-têtes sont définis sur autre chose que "*"
  • AllowAnyHeader est appelé : incluez au moins Accept, Content-Type, et Origin, ainsi que tous les en-têtes personnalisés que vous souhaitez prendre en charge.

Code de demande préliminaire automatique

Lorsque la stratégie CORS est appliquée , soit :

  • Globalement, en appelant app.UseCors dans Program.cs.
  • Utilisation de l’attribut [EnableCors] .

ASP.NET Core répond à la demande d’options préliminaires.

La section Test CORS de ce document illustre ce comportement.

Attribut [HttpOptions] pour les demandes préliminaires

Lorsque CORS est activé avec la stratégie appropriée, ASP.NET Core répond généralement automatiquement aux demandes préalables CORS.

Le code suivant utilise l’attribut [HttpOptions] pour créer des points de terminaison pour les requêtes OPTIONS :

[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
    // OPTIONS: api/TodoItems2/5
    [HttpOptions("{id}")]
    public IActionResult PreflightRoute(int id)
    {
        return NoContent();
    }

    // OPTIONS: api/TodoItems2 
    [HttpOptions]
    public IActionResult PreflightRoute()
    {
        return NoContent();
    }

    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return BadRequest();
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

Consultez Tester CORS avec l’attribut [EnableCors] et méthode RequireCors pour obtenir des instructions sur le test du code précédent.

Définir l’heure d’expiration de la préversion

L’en-tête Access-Control-Max-Age spécifie la durée pendant laquelle la réponse à la demande préliminaire peut être mise en cache. Pour définir cet en-tête, appelez SetPreflightMaxAge:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MySetPreflightExpirationPolicy",
        policy =>
        {
            policy.WithOrigins("http://example.com")
                   .SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Activer CORS sur un point de terminaison

Fonctionnement de CORS

Cette section décrit ce qui se passe dans une requête CORS au niveau des messages HTTP.

  • CORS n’est pas une fonctionnalité de sécurité. CORS est une norme W3C qui permet à un serveur d’assouplir la stratégie de même origine.
    • Par exemple, un acteur malveillant peut utiliser le script intersites (XSS) sur votre site et exécuter une demande intersites sur son site compatible CORS pour voler des informations.
  • Une API n’est pas plus sûre en autorisant CORS.
    • Il appartient au client (navigateur) d’appliquer CORS. Le serveur exécute la requête et retourne la réponse. C’est le client qui retourne une erreur et bloque la réponse. Par exemple, l’un des outils suivants affiche la réponse du serveur :
  • Il s’agit d’un moyen pour un serveur d’autoriser les navigateurs à exécuter une requête DHR oud’API Fetch inter-origines qui serait autrement interdite.
    • Les navigateurs sans CORS ne peuvent pas effectuer de requêtes cross-origin. Avant CORS, JSONP était utilisé pour contourner cette restriction. JSONP n’utilise pas XHR, il utilise la <script> balise pour recevoir la réponse. Les scripts sont autorisés à être chargés entre les origines.

La spécification CORS a introduit plusieurs nouveaux en-têtes HTTP qui activent les requêtes cross-origin. Si un navigateur prend en charge CORS, il définit automatiquement ces en-têtes pour les demandes cross-origin. Le code JavaScript personnalisé n’est pas nécessaire pour activer CORS.

Bouton put test sur l’exemple déployé

Voici un exemple de requête cross-origin à partir du bouton test Valeurs vers https://cors1.azurewebsites.net/api/values. En-tête Origin :

  • Fournit le domaine du site qui effectue la demande.
  • Est obligatoire et doit être différent de l’hôte.

En-têtes généraux

Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK

En-têtes de réponse

Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET

En-têtes de requête

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...

Dans OPTIONS les requêtes, le serveur définit l’en-tête d’en-têtesAccess-Control-Allow-Origin: {allowed origin} de réponse dans la réponse. Par exemple, l’exemple déployé, demande de bouton OPTIONSSupprimer [EnableCors] contient les en-têtes suivants :

En-têtes généraux

Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content

En-têtes de réponse

Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET

En-têtes de requête

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0

Dans les en-têtes de réponse précédents, le serveur définit l’en-tête Access-Control-Allow-Origin dans la réponse. La https://cors1.azurewebsites.net valeur de cet en-tête correspond à l’en-tête Origin de la requête.

Si AllowAnyOrigin est appelé, la Access-Control-Allow-Origin: *valeur générique , est retournée. AllowAnyOrigin autorise n’importe quelle origine.

Si la réponse n’inclut pas l’en-tête Access-Control-Allow-Origin , la demande cross-origin échoue. Plus précisément, le navigateur interdit la demande. Même si le serveur retourne une réponse réussie, le navigateur ne met pas la réponse à la disposition de l’application cliente.

La redirection HTTP vers HTTPS provoque des ERR_INVALID_REDIRECT sur la requête préliminaire CORS

Les demandes adressées à un point de terminaison à l’aide de HTTP qui sont redirigées vers HTTPS échouent UseHttpsRedirection avec ERR_INVALID_REDIRECT on the CORS preflight request.

Les projets d’API peuvent rejeter les requêtes HTTP au lieu de les utiliser UseHttpsRedirection pour rediriger les requêtes vers HTTPS.

CORS dans IIS

Lors du déploiement sur IIS, CORS doit s’exécuter avant l’authentification Windows si le serveur n’est pas configuré pour autoriser l’accès anonyme. Pour prendre en charge ce scénario, le module IIS CORS doit être installé et configuré pour l’application.

Tester CORS

L’exemple de téléchargement contient du code pour tester CORS. Consultez Guide pratique pour télécharger. L’exemple est un projet d’API avec Razor Pages ajouté :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "MyPolicy",
        policy =>
        {
            policy.WithOrigins("http://example.com",
                    "http://www.contoso.com",
                    "https://cors1.azurewebsites.net",
                    "https://cors3.azurewebsites.net",
                    "https://localhost:44398",
                    "https://localhost:5001")
                .WithMethods("PUT", "DELETE", "GET");
        });
});

builder.Services.AddControllers();
builder.Services.AddRazorPages();

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

app.MapControllers();
app.MapRazorPages();

app.Run();

Avertissement

WithOrigins("https://localhost:<port>"); doit uniquement être utilisé pour tester un exemple d’application similaire à l’exemple de code de téléchargement.

ValuesController Voici les points de terminaison à des fins de test :

[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public IActionResult Get() =>
        ControllerContext.MyDisplayRouteInfo();

    // GET api/values/5
    [HttpGet("{id}")]
    public IActionResult Get(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // PUT api/values/5
    [HttpPut("{id}")]
    public IActionResult Put(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);


    // GET: api/values/GetValues2
    [DisableCors]
    [HttpGet("{action}")]
    public IActionResult GetValues2() =>
        ControllerContext.MyDisplayRouteInfo();

}

MyDisplayRouteInfo est fourni par le package NuGet Rick.Docs.Samples.RouteInfo et affiche des informations sur l’itinéraire.

Testez l’exemple de code précédent en utilisant l’une des approches suivantes :

  • Utilisez l’exemple d’application déployé sur https://cors3.azurewebsites.net/. Il n’est pas nécessaire de télécharger l’exemple.
  • Exécutez l’exemple avec dotnet run à l’aide de l’URL par défaut de https://localhost:5001.
  • Exécutez l’exemple à partir de Visual Studio avec le port défini sur 44398 pour une URL de https://localhost:44398.

Utilisation d’un navigateur avec les outils F12 :

  • Sélectionnez le bouton Valeurs et passez en revue les en-têtes sous l’onglet Réseau .

  • Sélectionnez le bouton PUT test . Pour obtenir des instructions sur l’affichage de la demande OPTIONS, consultez Afficher les demandes OPTIONS . Le test PUT crée deux requêtes: une demande préliminaire OPTIONS et la requête PUT.

  • Sélectionnez le GetValues2 [DisableCors] bouton pour déclencher une demande CORS ayant échoué. Comme mentionné dans le document, la réponse renvoie 200 réussites, mais la demande CORS n’est pas effectuée. Sélectionnez l’onglet Console pour afficher l’erreur CORS. Selon le navigateur, une erreur similaire à ce qui suit s’affiche :

    L’accès à l’extraction à 'https://cors1.azurewebsites.net/api/values/GetValues2' partir de l’origine 'https://cors3.azurewebsites.net' a été bloqué par la stratégie CORS : aucun en-tête « Access-Control-Allow-Origin » n’est présent sur la ressource demandée. Si une réponse opaque répond à vos besoins, définissez le mode de la requête sur « no-cors » pour extraire la ressource avec CORS désactivé.

Les points de terminaison compatibles CORS peuvent être testés avec un outil, tel que curl, Fiddler ou Postman. Lors de l’utilisation d’un outil, l’origine de la requête spécifiée par l’en-tête Origin doit différer de celle de l’hôte qui reçoit la demande. Si la requête n’est pas d’origine croisée basée sur la valeur de l’en-tête Origin :

  • Il n’est pas nécessaire que CORS Middleware traite la demande.
  • Les en-têtes CORS ne sont pas retournés dans la réponse.

La commande suivante utilise curl pour émettre une requête OPTIONS avec des informations :

curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i

Tester CORS avec l’attribut [EnableCors] et la méthode RequireCors

Considérez le code suivant qui utilise le routage de point de terminaison pour activer CORS par point de terminaison à l’aide RequireCorsde :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "MyPolicy",
        policy =>
        {
            policy.WithOrigins("http://example.com",
                    "http://www.contoso.com",
                    "https://cors1.azurewebsites.net",
                    "https://cors3.azurewebsites.net",
                    "https://localhost:44398",
                    "https://localhost:5001")
                .WithMethods("PUT", "DELETE", "GET");
        });
});

builder.Services.AddControllers();
builder.Services.AddRazorPages();

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapGet("/echo",
        context => context.Response.WriteAsync("echo"))
        .RequireCors("MyPolicy");

    endpoints.MapControllers();
    endpoints.MapRazorPages();
});

app.Run();

Notez que seul le /echo point de terminaison utilise le pour autoriser les RequireCors demandes cross-origin à l’aide de la stratégie spécifiée. Les contrôleurs ci-dessous activent CORS à l’aide de l’attribut [EnableCors].

Les éléments suivants TodoItems1Controller fournissent des points de terminaison pour les tests :

[Route("api/[controller]")]
[ApiController]
public class TodoItems1Controller : ControllerBase 
{
    // PUT: api/TodoItems1/5
    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id) {
        if (id < 1) {
            return Content($"ID = {id}");
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

    // Delete: api/TodoItems1/5
    [HttpDelete("{id}")]
    public IActionResult MyDelete(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // GET: api/TodoItems1
    [HttpGet]
    public IActionResult GetTodoItems() =>
        ControllerContext.MyDisplayRouteInfo();

    [EnableCors("MyPolicy")]
    [HttpGet("{action}")]
    public IActionResult GetTodoItems2() =>
        ControllerContext.MyDisplayRouteInfo();

    // Delete: api/TodoItems1/MyDelete2/5
    [EnableCors("MyPolicy")]
    [HttpDelete("{action}/{id}")]
    public IActionResult MyDelete2(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);
}

Testez le code précédent à partir de la page de test de l’exemple déployé.

Les boutons Supprimer [EnableCors] et GET [EnableCors] réussissent, car les points de terminaison ont [EnableCors] et répondent aux demandes préliminaires. Les autres points de terminaison échouent. Le bouton GET échoue, car javaScript envoie :

 headers: {
      "Content-Type": "x-custom-header"
 },

Ce qui suit TodoItems2Controller fournit des points de terminaison similaires, mais inclut du code explicite pour répondre aux demandes OPTIONS :

[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
    // OPTIONS: api/TodoItems2/5
    [HttpOptions("{id}")]
    public IActionResult PreflightRoute(int id)
    {
        return NoContent();
    }

    // OPTIONS: api/TodoItems2 
    [HttpOptions]
    public IActionResult PreflightRoute()
    {
        return NoContent();
    }

    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return BadRequest();
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

    // [EnableCors] // Not needed as OPTIONS path provided.
    [HttpDelete("{id}")]
    public IActionResult MyDelete(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // [EnableCors] //  Warning ASP0023 Route '{id}' conflicts with another action route.
    //                  An HTTP request that matches multiple routes results in an ambiguous
    //                  match error.
    [EnableCors("MyPolicy")] // Required for this path.
    [HttpGet]
    public IActionResult GetTodoItems() =>
        ControllerContext.MyDisplayRouteInfo();

    [HttpGet("{action}")]
    public IActionResult GetTodoItems2() =>
        ControllerContext.MyDisplayRouteInfo();

    [EnableCors("MyPolicy")]  // Required for this path.
    [HttpDelete("{action}/{id}")]
    public IActionResult MyDelete2(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);
}

Testez le code précédent à partir de la page de test de l’exemple déployé. Dans la liste déroulante Contrôleur , sélectionnez Pré-vol , puis Définir le contrôleur. Tous les appels CORS aux points de TodoItems2Controller terminaison réussissent.

Ressources supplémentaires

Par Rick Anderson et Kirk Larkin

Cet article explique comment activer CORS dans une application ASP.NET Core.

La sécurité du navigateur empêche une page web d’envoyer des requêtes à un domaine différent de celui qui a servi la page web. Cette restriction est appelée stratégie de même origine. La stratégie de même origine empêche un site malveillant de lire des données sensibles à partir d’un autre site. Parfois, vous pouvez autoriser d’autres sites à effectuer des demandes d’origine croisée pour votre application. Pour plus d’informations, consultez l’article Mozilla CORS.

Partage des ressources d’origine croisée (CORS) :

  • Est une norme W3C qui permet à un serveur d’assouplir la stratégie de même origine.
  • N’est pas une fonctionnalité de sécurité, CORS assouplit la sécurité. Une API n’est pas plus sûre en autorisant CORS. Pour plus d’informations, consultez Fonctionnement de CORS.
  • Permet à un serveur d’autoriser explicitement certaines demandes d’origine croisée tout en rejetant d’autres.
  • Est plus sûr et plus flexible que les techniques antérieures, comme JSONP.

Affichez ou téléchargez l’exemple de code (procédure de téléchargement)

Même origine

Deux URL ont la même origine si elles ont des schémas, des hôtes et des ports identiques (RFC 6454).

Ces deux URL ont la même origine :

  • https://example.com/foo.html
  • https://example.com/bar.html

Ces URL ont des origines différentes des deux URL précédentes :

  • https://example.net: Domaine différent
  • https://www.example.com/foo.html: Sous-domaine différent
  • http://example.com/foo.html: Schéma différent
  • https://example.com:9000/foo.html: Port différent

Activez CORS

Il existe trois façons d’activer CORS :

L’utilisation de l’attribut [EnableCors] avec une stratégie nommée fournit le meilleur contrôle pour limiter les points de terminaison qui prennent en charge CORS.

Avertissement

UseCors doit être appelé dans l’ordre correct. Pour plus d’informations, consultez Ordre des intergiciels. Par exemple, UseCors doit être appelé avant UseResponseCaching lors de l’utilisation de UseResponseCaching.

Chaque approche est détaillée dans les sections suivantes.

CORS avec une stratégie et un intergiciel nommés

CORS Middleware gère les demandes d’origine croisée. Le code suivant applique une stratégie CORS à tous les points de terminaison de l’application avec les origines spécifiées :

var  MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      policy  =>
                      {
                          policy.WithOrigins("http://example.com",
                                              "http://www.contoso.com");
                      });
});

// services.AddResponseCaching();

builder.Services.AddControllers();

var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseCors(MyAllowSpecificOrigins);

app.UseAuthorization();

app.MapControllers();

app.Run();

Le code précédent :

Avec le routage de point de terminaison, le middleware CORS doit être configuré pour s’exécuter entre les appels à UseRouting et UseEndpoints.

Consultez Test CORS pour obtenir des instructions sur le test du code similaire au code précédent.

L’appel AddCors de méthode ajoute des services CORS au conteneur de service de l’application :

var  MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      policy  =>
                      {
                          policy.WithOrigins("http://example.com",
                                              "http://www.contoso.com");
                      });
});

// services.AddResponseCaching();

builder.Services.AddControllers();

var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseCors(MyAllowSpecificOrigins);

app.UseAuthorization();

app.MapControllers();

app.Run();

Pour plus d’informations, consultez Options de stratégie CORS dans ce document.

Les CorsPolicyBuilder méthodes peuvent être chaînées, comme indiqué dans le code suivant :

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(MyAllowSpecificOrigins,
                          policy =>
                          {
                              policy.WithOrigins("http://example.com",
                                                  "http://www.contoso.com")
                                                  .AllowAnyHeader()
                                                  .AllowAnyMethod();
                          });
});

builder.Services.AddControllers();

var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseCors(MyAllowSpecificOrigins);

app.UseAuthorization();

app.MapControllers();

app.Run();

Remarque : L’URL spécifiée ne doit pas contenir de barre oblique de fin (/). Si l’URL se termine par /, la comparaison retourne false et aucun en-tête n’est retourné.

Ordre des UseCors et des UseStaticFiles

En règle générale, UseStaticFiles est appelé avant UseCors. Les applications qui utilisent JavaScript pour récupérer des fichiers statiques intersite doivent appeler UseCors avant UseStaticFiles.

CORS avec stratégie et intergiciel par défaut

Le code mis en évidence suivant active la stratégie CORS par défaut :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(
        policy =>
        {
            policy.WithOrigins("http://example.com",
                                "http://www.contoso.com");
        });
});

builder.Services.AddControllers();

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

app.MapControllers();

app.Run();

Le code précédent applique la stratégie CORS par défaut à tous les points de terminaison du contrôleur.

Activer Cors avec le routage de point de terminaison

L’activation de CORS sur une base par point de terminaison à l’aide RequireCorsde ne prend pas en charge les demandes de contrôle préliminaire automatique. Pour plus d’informations, consultez ce problème GitHub et Test CORS avec routage de point de terminaison et [HttpOptions].

Avec le routage de point de terminaison, CORS peut être activé par point de terminaison à l’aide de l’ensemble RequireCors de méthodes d’extension :

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      policy =>
                      {
                          policy.WithOrigins("http://example.com",
                                              "http://www.contoso.com");
                      });
});

builder.Services.AddControllers();
builder.Services.AddRazorPages();

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapGet("/echo",
        context => context.Response.WriteAsync("echo"))
        .RequireCors(MyAllowSpecificOrigins);

    endpoints.MapControllers()
             .RequireCors(MyAllowSpecificOrigins);

    endpoints.MapGet("/echo2",
        context => context.Response.WriteAsync("echo2"));

    endpoints.MapRazorPages();
});

app.Run();

Dans le code précédent :

  • app.UseCors active le middleware CORS. Étant donné qu’aucune stratégie par défaut n’a été configurée, app.UseCors() elle seule n’active pas CORS.
  • Les /echo points de terminaison de contrôleur et autorisent les requêtes entre origines à l’aide de la stratégie spécifiée.
  • Les /echo2 points de terminaison pages et Razor n’autorisent pas les requêtes entre origines, car aucune stratégie par défaut n’a été spécifiée.

L’attribut [DisableCors] ne désactive pas CORS qui a été activé par le routage du point de terminaison avec RequireCors.

Consultez Test CORS avec routage de point de terminaison et [HttpOptions] pour obtenir des instructions sur le test du code similaire à ce qui précède.

Activer CORS avec des attributs

L’activation de CORS avec l’attribut [EnableCors] et l’application d’une stratégie nommée uniquement aux points de terminaison qui nécessitent CORS fournissent le meilleur contrôle.

L’attribut [EnableCors] offre une alternative à l’application globale de CORS. L’attribut [EnableCors] active CORS pour les points de terminaison sélectionnés, plutôt que pour tous les points de terminaison :

  • [EnableCors] spécifie la stratégie par défaut.
  • [EnableCors("{Policy String}")] spécifie une stratégie nommée.

L’attribut [EnableCors] peut être appliqué à :

  • Razor Page PageModel
  • Contrôleur
  • Méthode d’action du contrôleur

Différentes stratégies peuvent être appliquées à des contrôleurs, des modèles de page ou des méthodes d’action avec l’attribut [EnableCors] . Lorsque l’attribut [EnableCors] est appliqué à un contrôleur, un modèle de page ou une méthode d’action et que CORS est activé dans l’intergiciel, les deux stratégies sont appliquées. Nous vous déconseillons de combiner des stratégies. Utilisez le [EnableCors]attribut ou middleware, pas les deux dans la même application.

Le code suivant applique une stratégie différente à chaque méthode :

[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
    // GET api/values
    [EnableCors("AnotherPolicy")]
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
        return new string[] { "green widget", "red widget" };
    }

    // GET api/values/5
    [EnableCors("Policy1")]
    [HttpGet("{id}")]
    public ActionResult<string> Get(int id)
    {
        return id switch
        {
            1 => "green widget",
            2 => "red widget",
            _ => NotFound(),
        };
    }
}

Le code suivant crée deux stratégies CORS :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("Policy1",
        policy =>
        {
            policy.WithOrigins("http://example.com",
                                "http://www.contoso.com");
        });

    options.AddPolicy("AnotherPolicy",
        policy =>
        {
            policy.WithOrigins("http://www.contoso.com")
                                .AllowAnyHeader()
                                .AllowAnyMethod();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseRouting();

app.UseCors();

app.UseAuthorization();

app.MapControllers();

app.Run();

Pour le meilleur contrôle de la limitation des requêtes CORS :

  • Utilisez [EnableCors("MyPolicy")] avec une stratégie nommée.
  • Ne définissez pas de stratégie par défaut.
  • N’utilisez pas le routage de point de terminaison.

Le code de la section suivante correspond à la liste précédente.

Consultez Test CORS pour obtenir des instructions sur le test du code similaire au code précédent.

Désactiver CORS

L’attribut [DisableCors] ne désactive pas CORS qui a été activé par le routage du point de terminaison.

Le code suivant définit la stratégie "MyPolicy"CORS :

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "MyPolicy",
        policy =>
        {
            policy.WithOrigins("http://example.com",
                                "http://www.contoso.com")
                    .WithMethods("PUT", "DELETE", "GET");
        });
});

builder.Services.AddControllers();
builder.Services.AddRazorPages();

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

app.MapControllers();
app.MapRazorPages();

app.Run();

Le code suivant désactive CORS pour l’action GetValues2 :

[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public IActionResult Get() =>
        ControllerContext.MyDisplayRouteInfo();

    // GET api/values/5
    [HttpGet("{id}")]
    public IActionResult Get(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // PUT api/values/5
    [HttpPut("{id}")]
    public IActionResult Put(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);


    // GET: api/values/GetValues2
    [DisableCors]
    [HttpGet("{action}")]
    public IActionResult GetValues2() =>
        ControllerContext.MyDisplayRouteInfo();

}

Le code précédent :

Consultez Test CORS pour obtenir des instructions sur le test du code précédent.

Options de stratégie CORS

Cette section décrit les différentes options qui peuvent être définies dans une stratégie CORS :

AddPolicy est appelé dans Program.cs. Pour certaines options, il peut être utile de lire d’abord la section Fonctionnement de CORS .

Définir les origines autorisées

AllowAnyOrigin: autorise les requêtes CORS de toutes les origines avec n’importe quel schéma (http ou https). AllowAnyOrigin n’est pas sécurisé, car n’importe quel site web peut effectuer des demandes d’origine croisée à l’application.

Notes

La spécification de AllowAnyOrigin et AllowCredentials est une configuration non sécurisée et peut entraîner une falsification de requête intersites. Le service CORS retourne une réponse CORS non valide lorsqu’une application est configurée avec les deux méthodes.

AllowAnyOrigin affecte les demandes de contrôle préalable et l’en-tête Access-Control-Allow-Origin . Pour plus d’informations, consultez la section Demandes de contrôle préalable.

SetIsOriginAllowedToAllowWildcardSubdomains: définit la IsOriginAllowed propriété de la stratégie comme une fonction qui permet aux origines de correspondre à un domaine générique configuré lors de l’évaluation si l’origine est autorisée.

var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
        policy =>
        {
            policy.WithOrigins("https://*.example.com")
                .SetIsOriginAllowedToAllowWildcardSubdomains();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Définir les méthodes HTTP autorisées

AllowAnyMethod:

  • Autorise n’importe quelle méthode HTTP :
  • Affecte les demandes de contrôle préalable et l’en-tête Access-Control-Allow-Methods . Pour plus d’informations, consultez la section Demandes de contrôle préalable.

Définir les en-têtes de requête autorisés

Pour autoriser l’envoi d’en-têtes spécifiques dans une requête CORS, appelezWithHeaders et spécifiez les en-têtes autorisés :

using Microsoft.Net.Http.Headers;

var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
       policy =>
       {
           policy.WithOrigins("http://example.com")
                  .WithHeaders(HeaderNames.ContentType, "x-custom-header");
       });
});

builder.Services.AddControllers();

var app = builder.Build();

Pour autoriser tous les en-têtes de demande d’auteur, appelez AllowAnyHeader:

var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
        policy =>
        {
            policy.WithOrigins("https://*.example.com")
                   .AllowAnyHeader();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

AllowAnyHeader affecte les demandes préliminaires et l’en-tête Access-Control-Request-Headers . Pour plus d’informations, consultez la section Demandes de contrôle préalable.

Une correspondance de stratégie de middleware CORS à des en-têtes spécifiques spécifiés par WithHeaders n’est possible que lorsque les en-têtes envoyés correspondent Access-Control-Request-Headers exactement aux en-têtes indiqués dans WithHeaders.

Par exemple, considérez une application configurée comme suit :

app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));

CORS Middleware refuse une demande de contrôle préalable avec l’en-tête de requête suivant, car Content-Language (HeaderNames.ContentLanguage) n’est pas répertorié dans WithHeaders:

Access-Control-Request-Headers: Cache-Control, Content-Language

L’application retourne une réponse 200 OK , mais n’envoie pas les en-têtes CORS. Par conséquent, le navigateur ne tente pas la demande d’origine croisée.

Définir les en-têtes de réponse exposés

Par défaut, le navigateur n’expose pas tous les en-têtes de réponse à l’application. Pour plus d’informations, consultez Partage de ressources inter-origines W3C (terminologie) : en-tête de réponse simple.

Les en-têtes de réponse disponibles par défaut sont les suivants :

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

La spécification CORS appelle ces en-têtes de réponse simples. Pour rendre d’autres en-têtes disponibles pour l’application, appelez WithExposedHeaders:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MyExposeResponseHeadersPolicy",
        policy =>
        {
            policy.WithOrigins("https://*.example.com")
                   .WithExposedHeaders("x-custom-header");
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Informations d’identification dans les demandes inter-origines

Les informations d’identification nécessitent une gestion spéciale dans une requête CORS. Par défaut, le navigateur n’envoie pas d’informations d’identification avec une demande d’origine croisée. Les informations d’identification incluent des cookieschémas d’authentification s et HTTP. Pour envoyer des informations d’identification avec une demande d’origine croisée, le client doit définir XMLHttpRequest.withCredentials sur true.

Utilisation XMLHttpRequest directe :

var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;

Utilisation de jQuery :

$.ajax({
  type: 'get',
  url: 'https://www.example.com/api/test',
  xhrFields: {
    withCredentials: true
  }
});

Utilisation de l’API Fetch :

fetch('https://www.example.com/api/test', {
    credentials: 'include'
});

Le serveur doit autoriser les informations d’identification. Pour autoriser les informations d’identification inter-origines, appelez AllowCredentials:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MyMyAllowCredentialsPolicy",
        policy =>
        {
            policy.WithOrigins("http://example.com")
                   .AllowCredentials();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

La réponse HTTP comprend un Access-Control-Allow-Credentials en-tête, qui indique au navigateur que le serveur autorise les informations d’identification pour une demande d’origine croisée.

Si le navigateur envoie des informations d’identification mais que la réponse n’inclut pas d’en-tête valide Access-Control-Allow-Credentials , le navigateur n’expose pas la réponse à l’application et la demande d’origine croisée échoue.

L’autorisation des informations d’identification entre origines représente un risque de sécurité. Un site web d’un autre domaine peut envoyer les informations d’identification d’un utilisateur connecté à l’application pour le compte de l’utilisateur à l’insu de l’utilisateur.

La spécification CORS indique également que la définition des origines sur "*" (toutes les origines) n’est pas valide si l’en-tête Access-Control-Allow-Credentials est présent.

Demandes en pré-vol

Pour certaines requêtes CORS, le navigateur envoie une demande OPTIONS supplémentaire avant d’effectuer la demande réelle. Cette demande est appelée demande de contrôle préliminaire. Le navigateur peut ignorer la demande de pré-vol si toutes les conditions suivantes sont remplies :

  • La méthode de requête est GET, HEAD ou POST.
  • L’application ne définit pas d’en-têtes de requête autres que Accept, Accept-Language, Content-Language, Content-Typeou Last-Event-ID.
  • L’en-tête Content-Type , s’il est défini, a l’une des valeurs suivantes :
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

La règle sur les en-têtes de requête définis pour la demande cliente s’applique aux en-têtes que l’application définit en appelant setRequestHeader sur l’objet XMLHttpRequest . La spécification CORS appelle ces en-têtes des en-têtes de demande d’auteur. La règle ne s’applique pas aux en-têtes que le navigateur peut définir, tels que User-Agent, Hostou Content-Length.

Voici un exemple de réponse similaire à la demande de pré-vol effectuée à partir du bouton [Put test] dans la section Test CORS de ce document.

General:
Request URL: https://cors3.azurewebsites.net/api/values/5
Request Method: OPTIONS
Status Code: 204 No Content

Response Headers:
Access-Control-Allow-Methods: PUT,DELETE,GET
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f8...8;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Vary: Origin

Request Headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Method: PUT
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0

La demande de pré-vol utilise la méthode HTTP OPTIONS . Il peut inclure les en-têtes suivants :

Si la demande de préversion est refusée, l’application retourne une 200 OK réponse, mais ne définit pas les en-têtes CORS. Par conséquent, le navigateur ne tente pas la demande d’origine croisée. Pour obtenir un exemple de demande de pré-vol refusée, consultez la section Cors de test de ce document.

À l’aide des outils F12, l’application console affiche une erreur similaire à l’un des éléments suivants, en fonction du navigateur :

  • Firefox : Demande d’origine croisée bloquée : la même stratégie d’origine interdit la lecture de la ressource distante à l’adresse https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Raison : la demande CORS n’a pas abouti). En savoir plus
  • Chromium basé sur : L’accès à l’extraction à «https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 » à partir de l’origine «https://cors3.azurewebsites.net » a été bloqué par la stratégie CORS : la réponse à la demande de contrôle d’accès ne passe pas la vérification du contrôle d’accès : aucun en-tête « Access-Control-Allow-Origin » n’est présent sur la ressource demandée. Si une réponse opaque répond à vos besoins, définissez le mode de la requête sur « no-cors » pour extraire la ressource avec CORS désactivé.

Pour autoriser des en-têtes spécifiques, appelez WithHeaders:

using Microsoft.Net.Http.Headers;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MyAllowHeadersPolicy",
        policy =>
        {
        policy.WithOrigins("http://example.com")
                   .WithHeaders(HeaderNames.ContentType, "x-custom-header");
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Pour autoriser tous les en-têtes de demande d’auteur, appelez AllowAnyHeader:

using Microsoft.Net.Http.Headers;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MyAllowAllHeadersPolicy",
        policy =>
        {
            policy.WithOrigins("https://*.example.com")
                   .AllowAnyHeader();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Les navigateurs ne sont pas cohérents dans la façon dont ils définissent Access-Control-Request-Headers. Si :

  • Les en-têtes sont définis sur autre chose que "*"
  • AllowAnyHeader est appelé : incluez au moins Accept, Content-Typeet , ainsi Originque tous les en-têtes personnalisés que vous souhaitez prendre en charge.

Code de demande de contrôle préliminaire automatique

Lorsque la stratégie CORS est appliquée :

  • Globalement, en appelant app.UseCors dans Program.cs.
  • Utilisation de l’attribut [EnableCors] .

ASP.NET Core répond à la demande d’OPTIONS en pré-vol.

L’activation de CORS sur une base par point de terminaison à l’aide RequireCors actuellement ne prend pas en charge les demandes de contrôle préliminaire automatique.

La section Test CORS de ce document illustre ce comportement.

Attribut [HttpOptions] pour les demandes de contrôle préalable

Lorsque CORS est activé avec la stratégie appropriée, ASP.NET Core répond généralement automatiquement aux demandes de contrôle préalable CORS. Dans certains scénarios, ce n’est peut-être pas le cas. Par exemple, l’utilisation de CORS avec le routage de point de terminaison.

Le code suivant utilise l’attribut [HttpOptions] pour créer des points de terminaison pour les demandes OPTIONS :

[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
    // OPTIONS: api/TodoItems2/5
    [HttpOptions("{id}")]
    public IActionResult PreflightRoute(int id)
    {
        return NoContent();
    }

    // OPTIONS: api/TodoItems2 
    [HttpOptions]
    public IActionResult PreflightRoute()
    {
        return NoContent();
    }

    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return BadRequest();
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

Consultez Tester CORS avec routage de point de terminaison et [HttpOptions] pour obtenir des instructions sur le test du code précédent.

Définir l’heure d’expiration du contrôle préliminaire

L’en-tête Access-Control-Max-Age spécifie la durée pendant laquelle la réponse à la demande de contrôle préliminaire peut être mise en cache. Pour définir cet en-tête, appelez SetPreflightMaxAge:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MySetPreflightExpirationPolicy",
        policy =>
        {
            policy.WithOrigins("http://example.com")
                   .SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Fonctionnement de CORS

Cette section décrit ce qui se passe dans une requête CORS au niveau des messages HTTP.

  • CORS n’est pas une fonctionnalité de sécurité. CORS est une norme W3C qui permet à un serveur d’assouplir la stratégie de même origine.
    • Par exemple, un acteur malveillant peut utiliser des scripts intersite (XSS) sur votre site et exécuter une demande intersite à son site compatible CORS pour voler des informations.
  • Une API n’est pas plus sûre en autorisant CORS.
    • Il appartient au client (navigateur) d’appliquer CORS. Le serveur exécute la requête et retourne la réponse. C’est le client qui retourne une erreur et bloque la réponse. Par exemple, l’un des outils suivants affiche la réponse du serveur :
  • Il s’agit d’un moyen pour un serveur d’autoriser les navigateurs à exécuter une requête d’APIXHR ou Fetch d’origine croisée qui, sinon, serait interdite.
    • Les navigateurs sans CORS ne peuvent pas effectuer de requêtes inter-origines. Avant CORS, JSONP était utilisé pour contourner cette restriction. JSONP n’utilise pas XHR, il utilise la <script> balise pour recevoir la réponse. Les scripts sont autorisés à être chargés entre les origines.

La spécification CORS a introduit plusieurs nouveaux en-têtes HTTP qui activent les requêtes d’origine croisée. Si un navigateur prend en charge CORS, il définit automatiquement ces en-têtes pour les demandes d’origine croisée. Le code JavaScript personnalisé n’est pas nécessaire pour activer CORS.

Bouton de test PUT sur l’exemple déployé

Voici un exemple de demande d’origine croisée du bouton de test Valeurs vers https://cors1.azurewebsites.net/api/values. En-tête Origin :

  • Fournit le domaine du site qui effectue la demande.
  • Est obligatoire et doit être différent de l’hôte.

En-têtes généraux

Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK

En-têtes de réponse

Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET

En-têtes de requête

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...

Dans OPTIONS les requêtes, le serveur définit l’en-tête d’en-têtesAccess-Control-Allow-Origin: {allowed origin} de réponse dans la réponse. Par exemple, l’exemple déployé de demande de bouton OPTIONSSupprimer [EnableCors] contient les en-têtes suivants :

En-têtes généraux

Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content

En-têtes de réponse

Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET

En-têtes de requête

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0

Dans les en-têtes de réponse précédents, le serveur définit l’en-tête Access-Control-Allow-Origin dans la réponse. La https://cors1.azurewebsites.net valeur de cet en-tête correspond à l’en-tête Origin de la demande.

Si AllowAnyOrigin est appelé, la Access-Control-Allow-Origin: *valeur générique est retournée. AllowAnyOrigin autorise n’importe quelle origine.

Si la réponse n’inclut pas l’en-tête Access-Control-Allow-Origin , la demande d’origine croisée échoue. Plus précisément, le navigateur interdit la demande. Même si le serveur retourne une réponse réussie, le navigateur ne rend pas la réponse disponible pour l’application cliente.

La redirection HTTP vers HTTPS entraîne des ERR_INVALID_REDIRECT sur la demande de contrôle préalable CORS

Les demandes adressées à un point de terminaison utilisant HTTP qui sont redirigées vers HTTPS par UseHttpsRedirection échec avec ERR_INVALID_REDIRECT on the CORS preflight request.

Les projets d’API peuvent rejeter les requêtes HTTP au lieu de les utiliser UseHttpsRedirection pour rediriger les requêtes vers HTTPS.

Afficher les demandes OPTIONS

Par défaut, les navigateurs Chrome et Edge n’affichent pas les requêtes OPTIONS sous l’onglet réseau des outils F12. Pour afficher les demandes OPTIONS dans ces navigateurs :

  • chrome://flags/#out-of-blink-cors ou edge://flags/#out-of-blink-cors
  • désactivez l’indicateur.
  • redémarrer.

Firefox affiche les demandes OPTIONS par défaut.

CORS dans IIS

Lors du déploiement sur IIS, CORS doit s’exécuter avant l’authentification Windows si le serveur n’est pas configuré pour autoriser l’accès anonyme. Pour prendre en charge ce scénario, le module IIS CORS doit être installé et configuré pour l’application.

Tester CORS

L’exemple de téléchargement contient du code pour tester CORS. Consultez Guide pratique pour télécharger. L’exemple est un projet d’API avec Razor Pages ajoutées :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "MyPolicy",
                policy =>
                {
                    policy.WithOrigins("http://example.com",
                        "http://www.contoso.com",
                        "https://cors1.azurewebsites.net",
                        "https://cors3.azurewebsites.net",
                        "https://localhost:44398",
                        "https://localhost:5001")
                            .WithMethods("PUT", "DELETE", "GET");
                });
});

builder.Services.AddControllers();
builder.Services.AddRazorPages();

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

app.MapControllers();
app.MapRazorPages();

app.Run();

Avertissement

WithOrigins("https://localhost:<port>"); doit uniquement être utilisé pour tester un exemple d’application similaire à l’exemple de code de téléchargement.

ValuesController Voici les points de terminaison pour les tests :

[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public IActionResult Get() =>
        ControllerContext.MyDisplayRouteInfo();

    // GET api/values/5
    [HttpGet("{id}")]
    public IActionResult Get(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // PUT api/values/5
    [HttpPut("{id}")]
    public IActionResult Put(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);


    // GET: api/values/GetValues2
    [DisableCors]
    [HttpGet("{action}")]
    public IActionResult GetValues2() =>
        ControllerContext.MyDisplayRouteInfo();

}

MyDisplayRouteInfo est fourni par le package NuGet Rick.Docs.Samples.RouteInfo et affiche des informations sur l’itinéraire.

Testez l’exemple de code précédent à l’aide de l’une des approches suivantes :

  • Utilisez l’exemple d’application déployé à l’adresse https://cors3.azurewebsites.net/. Il n’est pas nécessaire de télécharger l’exemple.
  • Exécutez l’exemple avec dotnet run à l’aide de l’URL par défaut de https://localhost:5001.
  • Exécutez l’exemple à partir de Visual Studio avec le port défini sur 44398 pour une URL de https://localhost:44398.

Utilisation d’un navigateur avec les outils F12 :

  • Sélectionnez le bouton Valeurs et passez en revue les en-têtes sous l’onglet Réseau .

  • Sélectionnez le bouton PUT test . Consultez Afficher les demandes OPTIONS pour obtenir des instructions sur l’affichage de la demande OPTIONS. Le test PUT crée deux requêtes, une demande de contrôle préliminaire OPTIONS et la demande PUT.

  • Sélectionnez le GetValues2 [DisableCors] bouton pour déclencher une demande CORS ayant échoué. Comme indiqué dans le document, la réponse retourne 200 réussites, mais la demande CORS n’est pas effectuée. Sélectionnez l’onglet Console pour voir l’erreur CORS. Selon le navigateur, une erreur semblable à ce qui suit s’affiche :

    L’accès à l’extraction à 'https://cors1.azurewebsites.net/api/values/GetValues2' partir de l’origine 'https://cors3.azurewebsites.net' a été bloqué par la stratégie CORS : aucun en-tête « Access-Control-Allow-Origin » n’est présent sur la ressource demandée. Si une réponse opaque répond à vos besoins, définissez le mode de la requête sur « no-cors » pour extraire la ressource avec CORS désactivé.

Les points de terminaison compatibles CORS peuvent être testés avec un outil, comme curl, Fiddler ou Postman. Lors de l’utilisation d’un outil, l’origine de la requête spécifiée par l’en-tête Origin doit différer de celle de l’hôte qui reçoit la demande. Si la requête n’est pas d’origine croisée basée sur la valeur de l’en-tête Origin :

  • Il n’est pas nécessaire d’utiliser CORS Middleware pour traiter la demande.
  • Les en-têtes CORS ne sont pas retournés dans la réponse.

La commande suivante utilise curl pour émettre une demande OPTIONS avec des informations :

curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i

Tester CORS avec le routage des points de terminaison et [HttpOptions]

L’activation de CORS sur une base par point de terminaison à l’aide RequireCors actuellement ne prend pas en charge les demandes de contrôle préliminaire automatique. Considérez le code suivant qui utilise le routage de point de terminaison pour activer CORS :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "MyPolicy",
                policy =>
                {
                    policy.WithOrigins("http://example.com",
                        "http://www.contoso.com",
                        "https://cors1.azurewebsites.net",
                        "https://cors3.azurewebsites.net",
                        "https://localhost:44398",
                        "https://localhost:5001")
                            .WithMethods("PUT", "DELETE", "GET");
                });
});

builder.Services.AddControllers();
builder.Services.AddRazorPages();

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

app.MapControllers();
app.MapRazorPages();

app.Run();

Les éléments suivants TodoItems1Controller fournissent des points de terminaison pour les tests :

[Route("api/[controller]")]
[ApiController]
public class TodoItems1Controller : ControllerBase
{
    // PUT: api/TodoItems1/5
    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return Content($"ID = {id}");
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

    // Delete: api/TodoItems1/5
    [HttpDelete("{id}")]
    public IActionResult MyDelete(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // GET: api/TodoItems1
    [HttpGet]
    public IActionResult GetTodoItems() =>
        ControllerContext.MyDisplayRouteInfo();

    [EnableCors]
    [HttpGet("{action}")]
    public IActionResult GetTodoItems2() =>
        ControllerContext.MyDisplayRouteInfo();

    // Delete: api/TodoItems1/MyDelete2/5
    [EnableCors]
    [HttpDelete("{action}/{id}")]
    public IActionResult MyDelete2(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);
}

Testez le code précédent à partir de la page de test de l’exemple déployé.

Les boutons Supprimer [EnableCors] et GET [EnableCors] réussissent, car les points de terminaison ont [EnableCors] et répondent aux demandes de contrôle préalable. Les autres points de terminaison échouent. Le bouton GET échoue, car javaScript envoie :

 headers: {
      "Content-Type": "x-custom-header"
 },

Ce qui suit TodoItems2Controller fournit des points de terminaison similaires, mais inclut du code explicite pour répondre aux demandes OPTIONS :

[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
    // OPTIONS: api/TodoItems2/5
    [HttpOptions("{id}")]
    public IActionResult PreflightRoute(int id)
    {
        return NoContent();
    }

    // OPTIONS: api/TodoItems2 
    [HttpOptions]
    public IActionResult PreflightRoute()
    {
        return NoContent();
    }

    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return BadRequest();
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

    // [EnableCors] // Not needed as OPTIONS path provided
    [HttpDelete("{id}")]
    public IActionResult MyDelete(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    [EnableCors]  // Rquired for this path
    [HttpGet]
    public IActionResult GetTodoItems() =>
        ControllerContext.MyDisplayRouteInfo();

    [HttpGet("{action}")]
    public IActionResult GetTodoItems2() =>
        ControllerContext.MyDisplayRouteInfo();

    [EnableCors]  // Rquired for this path
    [HttpDelete("{action}/{id}")]
    public IActionResult MyDelete2(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);
}

Testez le code précédent à partir de la page de test de l’exemple déployé. Dans la liste déroulante Contrôleur , sélectionnez Pré-vol , puis Définir le contrôleur. Tous les appels CORS aux points de TodoItems2Controller terminaison réussissent.

Ressources supplémentaires

Par Rick Anderson et Kirk Larkin

Cet article explique comment activer CORS dans une application ASP.NET Core.

La sécurité du navigateur empêche une page web d’envoyer des requêtes à un domaine différent de celui qui a servi la page web. Cette restriction est appelée stratégie de même origine. La stratégie de même origine empêche un site malveillant de lire des données sensibles à partir d’un autre site. Parfois, vous pouvez autoriser d’autres sites à effectuer des demandes d’origine croisée pour votre application. Pour plus d’informations, consultez l’article Mozilla CORS.

Partage des ressources d’origine croisée (CORS) :

  • Est une norme W3C qui permet à un serveur d’assouplir la stratégie de même origine.
  • N’est pas une fonctionnalité de sécurité, CORS assouplit la sécurité. Une API n’est pas plus sûre en autorisant CORS. Pour plus d’informations, consultez Fonctionnement de CORS.
  • Permet à un serveur d’autoriser explicitement certaines demandes d’origine croisée tout en rejetant d’autres.
  • Est plus sûr et plus flexible que les techniques antérieures, comme JSONP.

Affichez ou téléchargez l’exemple de code (procédure de téléchargement)

Même origine

Deux URL ont la même origine si elles ont des schémas, des hôtes et des ports identiques (RFC 6454).

Ces deux URL ont la même origine :

  • https://example.com/foo.html
  • https://example.com/bar.html

Ces URL ont des origines différentes des deux URL précédentes :

  • https://example.net: Domaine différent
  • https://www.example.com/foo.html: Sous-domaine différent
  • http://example.com/foo.html: Schéma différent
  • https://example.com:9000/foo.html: Port différent

Activez CORS

Il existe trois façons d’activer CORS :

L’utilisation de l’attribut [EnableCors] avec une stratégie nommée fournit le meilleur contrôle pour limiter les points de terminaison qui prennent en charge CORS.

Avertissement

UseCors doit être appelé dans l’ordre correct. Pour plus d’informations, consultez Ordre des intergiciels. Par exemple, UseCors doit être appelé avant UseResponseCaching lors de l’utilisation de UseResponseCaching.

Chaque approche est détaillée dans les sections suivantes.

CORS avec une stratégie et un intergiciel nommés

CORS Middleware gère les demandes d’origine croisée. Le code suivant applique une stratégie CORS à tous les points de terminaison de l’application avec les origines spécifiées :

public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyAllowSpecificOrigins,
                              policy =>
                              {
                                  policy.WithOrigins("http://example.com",
                                                      "http://www.contoso.com");
                              });
        });

        // services.AddResponseCaching();
        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

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

        app.UseCors(MyAllowSpecificOrigins);

        // app.UseResponseCaching();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Le code précédent :

Avec le routage de point de terminaison, le middleware CORS doit être configuré pour s’exécuter entre les appels à UseRouting et UseEndpoints.

Consultez Test CORS pour obtenir des instructions sur le test du code similaire au code précédent.

L’appel AddCors de méthode ajoute des services CORS au conteneur de service de l’application :

public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyAllowSpecificOrigins,
                              policy =>
                              {
                                  policy.WithOrigins("http://example.com",
                                                      "http://www.contoso.com");
                              });
        });

        // services.AddResponseCaching();
        services.AddControllers();
    }

Pour plus d’informations, consultez Options de stratégie CORS dans ce document.

Les CorsPolicyBuilder méthodes peuvent être chaînées, comme indiqué dans le code suivant :

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy(MyAllowSpecificOrigins,
                          policy =>
                          {
                              policy.WithOrigins("http://example.com",
                                                  "http://www.contoso.com")
                                                  .AllowAnyHeader()
                                                  .AllowAnyMethod();
                          });
    });

    services.AddControllers();
}

Remarque : L’URL spécifiée ne doit pas contenir de barre oblique de fin (/). Si l’URL se termine par /, la comparaison retourne false et aucun en-tête n’est retourné.

CORS avec stratégie et intergiciel par défaut

Le code mis en évidence suivant active la stratégie CORS par défaut :

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddDefaultPolicy(
                policy =>
                {
                    policy.WithOrigins("http://example.com",
                                        "http://www.contoso.com");
                });
        });

        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

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

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Le code précédent applique la stratégie CORS par défaut à tous les points de terminaison du contrôleur.

Activer Cors avec le routage de point de terminaison

L’activation de CORS sur une base par point de terminaison à l’aide RequireCorsde ne prend pas en charge les demandes de contrôle préliminaire automatique. Pour plus d’informations, consultez ce problème GitHub et Test CORS avec routage de point de terminaison et [HttpOptions].

Avec le routage de point de terminaison, CORS peut être activé par point de terminaison à l’aide de l’ensemble RequireCors de méthodes d’extension :

public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyAllowSpecificOrigins,
                              policy =>
                              {
                                  policy.WithOrigins("http://example.com",
                                                      "http://www.contoso.com");
                              });
        });

        services.AddControllers();
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

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

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapGet("/echo",
                context => context.Response.WriteAsync("echo"))
                .RequireCors(MyAllowSpecificOrigins);

            endpoints.MapControllers()
                     .RequireCors(MyAllowSpecificOrigins);

            endpoints.MapGet("/echo2",
                context => context.Response.WriteAsync("echo2"));

            endpoints.MapRazorPages();
        });
    }
}

Dans le code précédent :

  • app.UseCors active le middleware CORS. Étant donné qu’aucune stratégie par défaut n’a été configurée, app.UseCors() elle seule n’active pas CORS.
  • Les /echo points de terminaison de contrôleur et autorisent les requêtes entre origines à l’aide de la stratégie spécifiée.
  • Les /echo2 points de terminaison pages et Razor n’autorisent pas les requêtes entre origines, car aucune stratégie par défaut n’a été spécifiée.

L’attribut [DisableCors] ne désactive pas CORS qui a été activé par le routage du point de terminaison avec RequireCors.

Consultez Test CORS avec routage de point de terminaison et [HttpOptions] pour obtenir des instructions sur le test du code similaire à ce qui précède.

Activer CORS avec des attributs

L’activation de CORS avec l’attribut [EnableCors] et l’application d’une stratégie nommée uniquement aux points de terminaison qui nécessitent CORS fournissent le meilleur contrôle.

L’attribut [EnableCors] offre une alternative à l’application globale de CORS. L’attribut [EnableCors] active CORS pour les points de terminaison sélectionnés, plutôt que pour tous les points de terminaison :

  • [EnableCors] spécifie la stratégie par défaut.
  • [EnableCors("{Policy String}")] spécifie une stratégie nommée.

L’attribut [EnableCors] peut être appliqué à :

  • Razor Page PageModel
  • Contrôleur
  • Méthode d’action du contrôleur

Différentes stratégies peuvent être appliquées à des contrôleurs, des modèles de page ou des méthodes d’action avec l’attribut [EnableCors] . Lorsque l’attribut [EnableCors] est appliqué à un contrôleur, un modèle de page ou une méthode d’action et que CORS est activé dans l’intergiciel, les deux stratégies sont appliquées. Nous vous déconseillons de combiner des stratégies. Utilisez le [EnableCors]attribut ou middleware, pas les deux dans la même application.

Le code suivant applique une stratégie différente à chaque méthode :

[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
    // GET api/values
    [EnableCors("AnotherPolicy")]
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
        return new string[] { "green widget", "red widget" };
    }

    // GET api/values/5
    [EnableCors("Policy1")]
    [HttpGet("{id}")]
    public ActionResult<string> Get(int id)
    {
        return id switch
        {
            1 => "green widget",
            2 => "red widget",
            _ => NotFound(),
        };
    }
}

Le code suivant crée deux stratégies CORS :

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

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy("Policy1",
                policy =>
                {
                    policy.WithOrigins("http://example.com",
                                        "http://www.contoso.com");
                });

            options.AddPolicy("AnotherPolicy",
                policy =>
                {
                    policy.WithOrigins("http://www.contoso.com")
                                        .AllowAnyHeader()
                                        .AllowAnyMethod();
                });
        });

        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Pour le meilleur contrôle de la limitation des requêtes CORS :

  • Utilisez [EnableCors("MyPolicy")] avec une stratégie nommée.
  • Ne définissez pas de stratégie par défaut.
  • N’utilisez pas le routage de point de terminaison.

Le code de la section suivante répond à la liste précédente.

Consultez Test CORS pour obtenir des instructions sur le test du code similaire au code précédent.

Désactiver CORS

L’attribut [DisableCors] ne désactive pas CORS qui a été activé par le routage du point de terminaison.

Le code suivant définit la stratégie CORS "MyPolicy":

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: "MyPolicy",
                policy =>
                {
                    policy.WithOrigins("http://example.com",
                                        "http://www.contoso.com")
                            .WithMethods("PUT", "DELETE", "GET");
                });
        });

        services.AddControllers();
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

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

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapRazorPages();
        });
    }
}

Le code suivant désactive CORS pour l’action GetValues2 :

[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public IActionResult Get() =>
        ControllerContext.MyDisplayRouteInfo();

    // GET api/values/5
    [HttpGet("{id}")]
    public IActionResult Get(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // PUT api/values/5
    [HttpPut("{id}")]
    public IActionResult Put(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);


    // GET: api/values/GetValues2
    [DisableCors]
    [HttpGet("{action}")]
    public IActionResult GetValues2() =>
        ControllerContext.MyDisplayRouteInfo();

}

Le code précédent :

Pour obtenir des instructions sur le test du code précédent, consultez Test CORS .

Options de stratégie CORS

Cette section décrit les différentes options qui peuvent être définies dans une stratégie CORS :

AddPolicy est appelé dans Startup.ConfigureServices. Pour certaines options, il peut être utile de lire d’abord la section Fonctionnement de CORS .

Définir les origines autorisées

AllowAnyOrigin: autorise les requêtes CORS de toutes les origines avec n’importe quel schéma (http ou https). AllowAnyOrigin n’est pas sécurisé, car n’importe quel site web peut effectuer des demandes cross-origin à l’application.

Notes

La spécification de AllowAnyOrigin et AllowCredentials est une configuration non sécurisée et peut entraîner une falsification de requête intersites. Le service CORS retourne une réponse CORS non valide lorsqu’une application est configurée avec les deux méthodes.

AllowAnyOrigin affecte les demandes préliminaires et l’en-tête Access-Control-Allow-Origin . Pour plus d’informations, consultez la section Demandes de contrôle préalable.

SetIsOriginAllowedToAllowWildcardSubdomains: définit la IsOriginAllowed propriété de la stratégie comme une fonction qui permet aux origines de correspondre à un domaine générique configuré lors de l’évaluation de l’autorisation de l’origine.

options.AddPolicy("MyAllowSubdomainPolicy",
    policy =>
    {
        policy.WithOrigins("https://*.example.com")
            .SetIsOriginAllowedToAllowWildcardSubdomains();
    });

Définir les méthodes HTTP autorisées

AllowAnyMethod:

  • Autorise n’importe quelle méthode HTTP :
  • Affecte les demandes préliminaires et l’en-tête Access-Control-Allow-Methods . Pour plus d’informations, consultez la section Demandes de contrôle préalable.

Définir les en-têtes de requête autorisés

Pour autoriser l’envoi d’en-têtes spécifiques dans une requête CORS, appelées en-têtes de demande d’auteur, appelez WithHeaders et spécifiez les en-têtes autorisés :

options.AddPolicy("MyAllowHeadersPolicy",
    policy =>
    {
        // requires using Microsoft.Net.Http.Headers;
        policy.WithOrigins("http://example.com")
               .WithHeaders(HeaderNames.ContentType, "x-custom-header");
    });

Pour autoriser tous les en-têtes de demande d’auteur, appelez AllowAnyHeader:

options.AddPolicy("MyAllowAllHeadersPolicy",
    policy =>
    {
        policy.WithOrigins("https://*.example.com")
               .AllowAnyHeader();
    });

AllowAnyHeader affecte les demandes préliminaires et l’en-tête Access-Control-Request-Headers . Pour plus d’informations, consultez la section Demandes de contrôle préalable.

Une stratégie d’intergiciel CORS correspond à des en-têtes spécifiques spécifiés par WithHeaders n’est possible que lorsque les en-têtes envoyés correspondent Access-Control-Request-Headers exactement aux en-têtes indiqués dans WithHeaders.

Par exemple, considérez une application configurée comme suit :

app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));

CORS Middleware refuse une demande préliminaire avec l’en-tête de requête suivant, car Content-Language (HeaderNames.ContentLanguage) n’est pas répertorié dans WithHeaders:

Access-Control-Request-Headers: Cache-Control, Content-Language

L’application retourne une réponse 200 OK , mais ne renvoie pas les en-têtes CORS. Par conséquent, le navigateur ne tente pas la requête cross-origin.

Définir les en-têtes de réponse exposés

Par défaut, le navigateur n’expose pas tous les en-têtes de réponse à l’application. Pour plus d’informations, consultez Partage de ressources cross-origin du W3C (terminologie) : en-tête de réponse simple.

Les en-têtes de réponse disponibles par défaut sont les suivants :

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

La spécification CORS appelle ces en-têtes de réponse simples. Pour rendre d’autres en-têtes disponibles pour l’application, appelez WithExposedHeaders:

options.AddPolicy("MyExposeResponseHeadersPolicy",
    policy =>
    {
        policy.WithOrigins("https://*.example.com")
               .WithExposedHeaders("x-custom-header");
    });

Informations d’identification dans les demandes cross-origin

Les informations d’identification nécessitent une gestion spéciale dans une demande CORS. Par défaut, le navigateur n’envoie pas d’informations d’identification avec une requête cross-origin. Les informations d’identification incluent des cookieschémas d’authentification s et HTTP. Pour envoyer des informations d’identification avec une requête cross-origin, le client doit définir XMLHttpRequest.withCredentials sur true.

En utilisant XMLHttpRequest directement :

var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;

Utilisation de jQuery :

$.ajax({
  type: 'get',
  url: 'https://www.example.com/api/test',
  xhrFields: {
    withCredentials: true
  }
});

Utilisation de l’API Fetch :

fetch('https://www.example.com/api/test', {
    credentials: 'include'
});

Le serveur doit autoriser les informations d’identification. Pour autoriser les informations d’identification cross-origin, appelez AllowCredentials:

options.AddPolicy("MyMyAllowCredentialsPolicy",
    policy =>
    {
        policy.WithOrigins("http://example.com")
               .AllowCredentials();
    });

La réponse HTTP inclut un Access-Control-Allow-Credentials en-tête, qui indique au navigateur que le serveur autorise les informations d’identification pour une requête cross-origin.

Si le navigateur envoie des informations d’identification mais que la réponse n’inclut pas d’en-tête valide Access-Control-Allow-Credentials , le navigateur n’expose pas la réponse à l’application et la demande cross-origin échoue.

L’autorisation des informations d’identification cross-origin est un risque pour la sécurité. Un site web d’un autre domaine peut envoyer les informations d’identification d’un utilisateur connecté à l’application pour le compte de l’utilisateur à l’insu de l’utilisateur.

La spécification CORS indique également que la définition des origines sur "*" (toutes les origines) n’est pas valide si l’en-tête Access-Control-Allow-Credentials est présent.

Demandes préliminaires

Pour certaines requêtes CORS, le navigateur envoie une requête OPTIONS supplémentaire avant d’effectuer la requête réelle. Cette requête est appelée demande préliminaire. Le navigateur peut ignorer la demande préliminaire si toutes les conditions suivantes sont remplies :

  • La méthode de requête est GET, HEAD ou POST.
  • L’application ne définit pas d’en-têtes de requête autres que Accept, Accept-Language, Content-Language, Content-Typeou Last-Event-ID.
  • L’en-tête Content-Type , s’il est défini, a l’une des valeurs suivantes :
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

La règle sur les en-têtes de requête définis pour la demande cliente s’applique aux en-têtes définis par l’application en appelant setRequestHeader sur l’objet XMLHttpRequest . La spécification CORS appelle ces en-têtes des en-têtes de demande d’auteur. La règle ne s’applique pas aux en-têtes que le navigateur peut définir, tels que User-Agent, Hostou Content-Length.

Voici un exemple de réponse similaire à la demande préliminaire effectuée à partir du bouton [Put test] dans la section Test CORS de ce document.

General:
Request URL: https://cors3.azurewebsites.net/api/values/5
Request Method: OPTIONS
Status Code: 204 No Content

Response Headers:
Access-Control-Allow-Methods: PUT,DELETE,GET
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f8...8;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Vary: Origin

Request Headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Method: PUT
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0

La requête préliminaire utilise la méthode HTTP OPTIONS . Il peut inclure les en-têtes suivants :

Si la demande préliminaire est refusée, l’application retourne une 200 OK réponse, mais ne définit pas les en-têtes CORS. Par conséquent, le navigateur ne tente pas la requête cross-origin. Pour obtenir un exemple de demande préliminaire refusée, consultez la section Test CORS de ce document.

À l’aide des outils F12, l’application console affiche une erreur similaire à l’une des suivantes, selon le navigateur :

  • Firefox : Requête cross-origin bloquée : la même stratégie d’origine interdit la lecture de la ressource distante à l’adresse https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Motif : la demande CORS n’a pas réussi). En savoir plus
  • Chromium basé sur : l’accès à l’extraction à partirhttps://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 de l’origine ahttps://cors3.azurewebsites.net été bloqué par la stratégie CORS : La réponse à la demande préliminaire ne passe pas la vérification du contrôle d’accès : aucun en-tête « Access-Control-Allow-Origin » n’est présent sur la ressource demandée. Si une réponse opaque répond à vos besoins, définissez le mode de la requête sur « no-cors » pour extraire la ressource avec CORS désactivé.

Pour autoriser des en-têtes spécifiques, appelez WithHeaders:

options.AddPolicy("MyAllowHeadersPolicy",
    policy =>
    {
        // requires using Microsoft.Net.Http.Headers;
        policy.WithOrigins("http://example.com")
               .WithHeaders(HeaderNames.ContentType, "x-custom-header");
    });

Pour autoriser tous les en-têtes de demande d’auteur, appelez AllowAnyHeader:

options.AddPolicy("MyAllowAllHeadersPolicy",
    policy =>
    {
        policy.WithOrigins("https://*.example.com")
               .AllowAnyHeader();
    });

Les navigateurs ne sont pas cohérents dans la façon dont ils définissent Access-Control-Request-Headers. Si l’un des deux :

  • Les en-têtes sont définis sur autre chose que "*"
  • AllowAnyHeader est appelé : incluez au moins Accept, Content-Type, et Origin, ainsi que tous les en-têtes personnalisés que vous souhaitez prendre en charge.

Code de demande préliminaire automatique

Lorsque la stratégie CORS est appliquée , soit :

  • Globalement, en appelant app.UseCors dans Startup.Configure.
  • Utilisation de l’attribut [EnableCors] .

ASP.NET Core répond à la demande d’options préliminaires.

L’activation de CORS par point de terminaison à l’aide RequireCors de ne prend actuellement pas en charge les demandes préliminaires automatiques.

La section Test CORS de ce document illustre ce comportement.

Attribut [HttpOptions] pour les demandes préliminaires

Lorsque CORS est activé avec la stratégie appropriée, ASP.NET Core répond généralement automatiquement aux demandes préalables CORS. Dans certains scénarios, cela peut ne pas être le cas. Par exemple, l’utilisation de CORS avec le routage de point de terminaison.

Le code suivant utilise l’attribut [HttpOptions] pour créer des points de terminaison pour les requêtes OPTIONS :

[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
    // OPTIONS: api/TodoItems2/5
    [HttpOptions("{id}")]
    public IActionResult PreflightRoute(int id)
    {
        return NoContent();
    }

    // OPTIONS: api/TodoItems2 
    [HttpOptions]
    public IActionResult PreflightRoute()
    {
        return NoContent();
    }

    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return BadRequest();
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

Pour obtenir des instructions sur le test du code précédent, consultez Test CORS avec routage de point de terminaison et [HttpOptions ].

Définir l’heure d’expiration de la préversion

L’en-tête Access-Control-Max-Age spécifie la durée pendant laquelle la réponse à la demande préliminaire peut être mise en cache. Pour définir cet en-tête, appelez SetPreflightMaxAge:

options.AddPolicy("MySetPreflightExpirationPolicy",
    policy =>
    {
        policy.WithOrigins("http://example.com")
               .SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
    });

Fonctionnement de CORS

Cette section décrit ce qui se passe dans une requête CORS au niveau des messages HTTP.

  • CORS n’est pas une fonctionnalité de sécurité. CORS est une norme W3C qui permet à un serveur d’assouplir la stratégie de même origine.
    • Par exemple, un acteur malveillant peut utiliser le script intersites (XSS) sur votre site et exécuter une demande intersites sur son site compatible CORS pour voler des informations.
  • Une API n’est pas plus sûre en autorisant CORS.
    • Il appartient au client (navigateur) d’appliquer CORS. Le serveur exécute la requête et retourne la réponse. C’est le client qui retourne une erreur et bloque la réponse. Par exemple, l’un des outils suivants affiche la réponse du serveur :
  • Il s’agit d’un moyen pour un serveur d’autoriser les navigateurs à exécuter une requête DHR oud’API Fetch inter-origines qui serait autrement interdite.
    • Les navigateurs sans CORS ne peuvent pas effectuer de requêtes cross-origin. Avant CORS, JSONP était utilisé pour contourner cette restriction. JSONP n’utilise pas XHR, il utilise la <script> balise pour recevoir la réponse. Les scripts sont autorisés à être chargés entre les origines.

La spécification CORS a introduit plusieurs nouveaux en-têtes HTTP qui activent les requêtes cross-origin. Si un navigateur prend en charge CORS, il définit automatiquement ces en-têtes pour les demandes cross-origin. Le code JavaScript personnalisé n’est pas nécessaire pour activer CORS.

Bouton put test sur l’exemple déployé

Voici un exemple de requête cross-origin à partir du bouton test Valeurs vers https://cors1.azurewebsites.net/api/values. En-tête Origin :

  • Fournit le domaine du site qui effectue la demande.
  • Est obligatoire et doit être différent de l’hôte.

En-têtes généraux

Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK

En-têtes de réponse

Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET

En-têtes de requête

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...

Dans OPTIONS les requêtes, le serveur définit l’en-tête d’en-têtesAccess-Control-Allow-Origin: {allowed origin} de réponse dans la réponse. Par exemple, l’exemple déployé, demande de bouton OPTIONSSupprimer [EnableCors] contient les en-têtes suivants :

En-têtes généraux

Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content

En-têtes de réponse

Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET

En-têtes de requête

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0

Dans les en-têtes de réponse précédents, le serveur définit l’en-tête Access-Control-Allow-Origin dans la réponse. La https://cors1.azurewebsites.net valeur de cet en-tête correspond à l’en-tête Origin de la requête.

Si AllowAnyOrigin est appelé, la Access-Control-Allow-Origin: *valeur générique , est retournée. AllowAnyOrigin autorise n’importe quelle origine.

Si la réponse n’inclut pas l’en-tête Access-Control-Allow-Origin , la demande cross-origin échoue. Plus précisément, le navigateur interdit la demande. Même si le serveur retourne une réponse réussie, le navigateur ne met pas la réponse à la disposition de l’application cliente.

Afficher les requêtes OPTIONS

Par défaut, les navigateurs Chrome et Edge n’affichent pas les requêtes OPTIONS sous l’onglet réseau des outils F12. Pour afficher les demandes OPTIONS dans ces navigateurs :

  • chrome://flags/#out-of-blink-cors ou edge://flags/#out-of-blink-cors
  • désactivez l’indicateur.
  • redémarrer.

Firefox affiche les requêtes OPTIONS par défaut.

CORS dans IIS

Lors du déploiement sur IIS, CORS doit s’exécuter avant l’authentification Windows si le serveur n’est pas configuré pour autoriser l’accès anonyme. Pour prendre en charge ce scénario, le module IIS CORS doit être installé et configuré pour l’application.

Tester CORS

L’exemple de téléchargement contient du code pour tester CORS. Consultez Guide pratique pour télécharger. L’exemple est un projet d’API avec Razor Pages ajouté :

public class StartupTest2
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: "MyPolicy",
                policy =>
                {
                    policy.WithOrigins("http://example.com",
                        "http://www.contoso.com",
                        "https://cors1.azurewebsites.net",
                        "https://cors3.azurewebsites.net",
                        "https://localhost:44398",
                        "https://localhost:5001")
                            .WithMethods("PUT", "DELETE", "GET");
                });
        });

        services.AddControllers();
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapRazorPages();
        });
    }
}

Avertissement

WithOrigins("https://localhost:<port>"); doit uniquement être utilisé pour tester un exemple d’application similaire à l’exemple de code de téléchargement.

ValuesController Voici les points de terminaison à des fins de test :

[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public IActionResult Get() =>
        ControllerContext.MyDisplayRouteInfo();

    // GET api/values/5
    [HttpGet("{id}")]
    public IActionResult Get(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // PUT api/values/5
    [HttpPut("{id}")]
    public IActionResult Put(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);


    // GET: api/values/GetValues2
    [DisableCors]
    [HttpGet("{action}")]
    public IActionResult GetValues2() =>
        ControllerContext.MyDisplayRouteInfo();

}

MyDisplayRouteInfo est fourni par le package NuGet Rick.Docs.Samples.RouteInfo et affiche des informations sur l’itinéraire.

Testez l’exemple de code précédent en utilisant l’une des approches suivantes :

  • Utilisez l’exemple d’application déployé sur https://cors3.azurewebsites.net/. Il n’est pas nécessaire de télécharger l’exemple.
  • Exécutez l’exemple avec dotnet run à l’aide de l’URL par défaut de https://localhost:5001.
  • Exécutez l’exemple à partir de Visual Studio avec le port défini sur 44398 pour une URL de https://localhost:44398.

Utilisation d’un navigateur avec les outils F12 :

  • Sélectionnez le bouton Valeurs et passez en revue les en-têtes sous l’onglet Réseau .

  • Sélectionnez le bouton PUT test . Pour obtenir des instructions sur l’affichage de la demande OPTIONS, consultez Afficher les demandes OPTIONS . Le test PUT crée deux requêtes: une demande préliminaire OPTIONS et la requête PUT.

  • Sélectionnez le GetValues2 [DisableCors] bouton pour déclencher une demande CORS ayant échoué. Comme mentionné dans le document, la réponse renvoie 200 réussites, mais la demande CORS n’est pas effectuée. Sélectionnez l’onglet Console pour afficher l’erreur CORS. Selon le navigateur, une erreur similaire à ce qui suit s’affiche :

    L’accès à l’extraction à 'https://cors1.azurewebsites.net/api/values/GetValues2' partir de l’origine 'https://cors3.azurewebsites.net' a été bloqué par la stratégie CORS : aucun en-tête « Access-Control-Allow-Origin » n’est présent sur la ressource demandée. Si une réponse opaque répond à vos besoins, définissez le mode de la requête sur « no-cors » pour extraire la ressource avec CORS désactivé.

Les points de terminaison compatibles CORS peuvent être testés avec un outil, tel que curl, Fiddler ou Postman. Lors de l’utilisation d’un outil, l’origine de la requête spécifiée par l’en-tête Origin doit différer de celle de l’hôte qui reçoit la demande. Si la requête n’est pas d’origine croisée basée sur la valeur de l’en-tête Origin :

  • Il n’est pas nécessaire que CORS Middleware traite la demande.
  • Les en-têtes CORS ne sont pas retournés dans la réponse.

La commande suivante utilise curl pour émettre une demande OPTIONS avec des informations :

curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i

Tester CORS avec le routage des points de terminaison et [HttpOptions]

L’activation de CORS sur une base par point de terminaison à l’aide RequireCors actuellement ne prend pas en charge les demandes de contrôle préliminaire automatique. Considérez le code suivant qui utilise le routage de point de terminaison pour activer CORS :

public class StartupEndPointBugTest
{
    readonly string MyPolicy = "_myPolicy";

    // .WithHeaders(HeaderNames.ContentType, "x-custom-header")
    // forces browsers to require a preflight request with GET

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyPolicy,
                policy =>
                {
                    policy.WithOrigins("http://example.com",
                                        "http://www.contoso.com",
                                        "https://cors1.azurewebsites.net",
                                        "https://cors3.azurewebsites.net",
                                        "https://localhost:44398",
                                        "https://localhost:5001")
                           .WithHeaders(HeaderNames.ContentType, "x-custom-header")
                           .WithMethods("PUT", "DELETE", "GET", "OPTIONS");
                });
        });

        services.AddControllers();
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers().RequireCors(MyPolicy);
            endpoints.MapRazorPages();
        });
    }
}

Les éléments suivants TodoItems1Controller fournissent des points de terminaison pour les tests :

[Route("api/[controller]")]
[ApiController]
public class TodoItems1Controller : ControllerBase
{
    // PUT: api/TodoItems1/5
    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return Content($"ID = {id}");
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

    // Delete: api/TodoItems1/5
    [HttpDelete("{id}")]
    public IActionResult MyDelete(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // GET: api/TodoItems1
    [HttpGet]
    public IActionResult GetTodoItems() =>
        ControllerContext.MyDisplayRouteInfo();

    [EnableCors]
    [HttpGet("{action}")]
    public IActionResult GetTodoItems2() =>
        ControllerContext.MyDisplayRouteInfo();

    // Delete: api/TodoItems1/MyDelete2/5
    [EnableCors]
    [HttpDelete("{action}/{id}")]
    public IActionResult MyDelete2(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);
}

Testez le code précédent à partir de la page de test de l’exemple déployé.

Les boutons Supprimer [EnableCors] et GET [EnableCors] réussissent, car les points de terminaison ont [EnableCors] et répondent aux demandes de contrôle préalable. Les autres points de terminaison échouent. Le bouton GET échoue, car javaScript envoie :

 headers: {
      "Content-Type": "x-custom-header"
 },

Ce qui suit TodoItems2Controller fournit des points de terminaison similaires, mais inclut du code explicite pour répondre aux demandes OPTIONS :

[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
    // OPTIONS: api/TodoItems2/5
    [HttpOptions("{id}")]
    public IActionResult PreflightRoute(int id)
    {
        return NoContent();
    }

    // OPTIONS: api/TodoItems2 
    [HttpOptions]
    public IActionResult PreflightRoute()
    {
        return NoContent();
    }

    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return BadRequest();
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

    // [EnableCors] // Not needed as OPTIONS path provided
    [HttpDelete("{id}")]
    public IActionResult MyDelete(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    [EnableCors]  // Rquired for this path
    [HttpGet]
    public IActionResult GetTodoItems() =>
        ControllerContext.MyDisplayRouteInfo();

    [HttpGet("{action}")]
    public IActionResult GetTodoItems2() =>
        ControllerContext.MyDisplayRouteInfo();

    [EnableCors]  // Rquired for this path
    [HttpDelete("{action}/{id}")]
    public IActionResult MyDelete2(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);
}

Testez le code précédent à partir de la page de test de l’exemple déployé. Dans la liste déroulante Contrôleur , sélectionnez Pré-vol , puis Définir le contrôleur. Tous les appels CORS aux points de TodoItems2Controller terminaison réussissent.

Ressources supplémentaires

Par Rick Anderson et Kirk Larkin

Cet article explique comment activer CORS dans une application ASP.NET Core.

La sécurité du navigateur empêche une page web d’envoyer des requêtes à un domaine différent de celui qui a servi la page web. Cette restriction est appelée stratégie de même origine. La stratégie de même origine empêche un site malveillant de lire des données sensibles à partir d’un autre site. Parfois, vous pouvez autoriser d’autres sites à effectuer des demandes d’origine croisée pour votre application. Pour plus d’informations, consultez l’article Mozilla CORS.

Partage des ressources d’origine croisée (CORS) :

  • Est une norme W3C qui permet à un serveur d’assouplir la stratégie de même origine.
  • N’est pas une fonctionnalité de sécurité, CORS assouplit la sécurité. Une API n’est pas plus sûre en autorisant CORS. Pour plus d’informations, consultez Fonctionnement de CORS.
  • Permet à un serveur d’autoriser explicitement certaines demandes d’origine croisée tout en rejetant d’autres.
  • Est plus sûr et plus flexible que les techniques antérieures, comme JSONP.

Affichez ou téléchargez l’exemple de code (procédure de téléchargement)

Même origine

Deux URL ont la même origine si elles ont des schémas, des hôtes et des ports identiques (RFC 6454).

Ces deux URL ont la même origine :

  • https://example.com/foo.html
  • https://example.com/bar.html

Ces URL ont des origines différentes des deux URL précédentes :

  • https://example.net: Domaine différent
  • https://www.example.com/foo.html: Sous-domaine différent
  • http://example.com/foo.html: Schéma différent
  • https://example.com:9000/foo.html: Port différent

Activez CORS

Il existe trois façons d’activer CORS :

L’utilisation de l’attribut [EnableCors] avec une stratégie nommée fournit le meilleur contrôle pour limiter les points de terminaison qui prennent en charge CORS.

Avertissement

UseCors doit être appelé dans l’ordre correct. Pour plus d’informations, consultez Ordre des intergiciels. Par exemple, UseCors doit être appelé avant UseResponseCaching lors de l’utilisation de UseResponseCaching.

Chaque approche est détaillée dans les sections suivantes.

CORS avec une stratégie et un intergiciel nommés

CORS Middleware gère les demandes d’origine croisée. Le code suivant applique une stratégie CORS à tous les points de terminaison de l’application avec les origines spécifiées :

var  MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      policy  =>
                      {
                          policy.WithOrigins("http://example.com",
                                              "http://www.contoso.com");
                      });
});

// services.AddResponseCaching();

builder.Services.AddControllers();

var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseCors(MyAllowSpecificOrigins);

app.UseAuthorization();

app.MapControllers();

app.Run();

Le code précédent :

Avec le routage de point de terminaison, le middleware CORS doit être configuré pour s’exécuter entre les appels à UseRouting et UseEndpoints.

Consultez Test CORS pour obtenir des instructions sur le test du code similaire au code précédent.

L’appel AddCors de méthode ajoute des services CORS au conteneur de service de l’application :

var  MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      policy  =>
                      {
                          policy.WithOrigins("http://example.com",
                                              "http://www.contoso.com");
                      });
});

// services.AddResponseCaching();

builder.Services.AddControllers();

var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseCors(MyAllowSpecificOrigins);

app.UseAuthorization();

app.MapControllers();

app.Run();

Pour plus d’informations, consultez Options de stratégie CORS dans ce document.

Les CorsPolicyBuilder méthodes peuvent être chaînées, comme indiqué dans le code suivant :

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(MyAllowSpecificOrigins,
                          policy =>
                          {
                              policy.WithOrigins("http://example.com",
                                                  "http://www.contoso.com")
                                                  .AllowAnyHeader()
                                                  .AllowAnyMethod();
                          });
});

builder.Services.AddControllers();

var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseCors(MyAllowSpecificOrigins);

app.UseAuthorization();

app.MapControllers();

app.Run();

Remarque : L’URL spécifiée ne doit pas contenir de barre oblique de fin (/). Si l’URL se termine par /, la comparaison retourne false et aucun en-tête n’est retourné.

Ordre des UseCors et des UseStaticFiles

En règle générale, UseStaticFiles est appelé avant UseCors. Les applications qui utilisent JavaScript pour récupérer des fichiers statiques intersite doivent appeler UseCors avant UseStaticFiles.

CORS avec stratégie et intergiciel par défaut

Le code mis en évidence suivant active la stratégie CORS par défaut :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(
        policy =>
        {
            policy.WithOrigins("http://example.com",
                                "http://www.contoso.com");
        });
});

builder.Services.AddControllers();

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

app.MapControllers();

app.Run();

Le code précédent applique la stratégie CORS par défaut à tous les points de terminaison du contrôleur.

Activer Cors avec le routage de point de terminaison

Avec le routage de point de terminaison, CORS peut être activé par point de terminaison à l’aide de l’ensemble RequireCors de méthodes d’extension :

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      policy =>
                      {
                          policy.WithOrigins("http://example.com",
                                              "http://www.contoso.com");
                      });
});

builder.Services.AddControllers();
builder.Services.AddRazorPages();

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapGet("/echo",
        context => context.Response.WriteAsync("echo"))
        .RequireCors(MyAllowSpecificOrigins);

    endpoints.MapControllers()
             .RequireCors(MyAllowSpecificOrigins);

    endpoints.MapGet("/echo2",
        context => context.Response.WriteAsync("echo2"));

    endpoints.MapRazorPages();
});

app.Run();

Dans le code précédent :

  • app.UseCors active le middleware CORS. Étant donné qu’aucune stratégie par défaut n’a été configurée, app.UseCors() elle seule n’active pas CORS.
  • Les /echo points de terminaison de contrôleur et autorisent les requêtes entre origines à l’aide de la stratégie spécifiée.
  • Les /echo2 points de terminaison pages et Razor n’autorisent pas les requêtes entre origines, car aucune stratégie par défaut n’a été spécifiée.

L’attribut [DisableCors] ne désactive pas CORS qui a été activé par le routage du point de terminaison avec RequireCors.

Dans ASP.NET Core 7.0, l’attribut [EnableCors] doit passer un paramètre ou un avertissement ASP0023 est généré à partir d’une correspondance ambiguë sur l’itinéraire. ASP.NET Core 8.0 et versions ultérieures ne génère pas l’avertissementASP0023.

[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
    // OPTIONS: api/TodoItems2/5
    [HttpOptions("{id}")]
    public IActionResult PreflightRoute(int id)
    {
        return NoContent();
    }

    // OPTIONS: api/TodoItems2 
    [HttpOptions]
    public IActionResult PreflightRoute()
    {
        return NoContent();
    }

    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return BadRequest();
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

    // [EnableCors] // Not needed as OPTIONS path provided.
    [HttpDelete("{id}")]
    public IActionResult MyDelete(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // [EnableCors] //  Warning ASP0023 Route '{id}' conflicts with another action route.
    //                  An HTTP request that matches multiple routes results in an ambiguous
    //                  match error.
    [EnableCors("MyPolicy")] // Required for this path.
    [HttpGet]
    public IActionResult GetTodoItems() =>
        ControllerContext.MyDisplayRouteInfo();

    [HttpGet("{action}")]
    public IActionResult GetTodoItems2() =>
        ControllerContext.MyDisplayRouteInfo();

    [EnableCors("MyPolicy")]  // Required for this path.
    [HttpDelete("{action}/{id}")]
    public IActionResult MyDelete2(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);
}

Pour obtenir des instructions sur le test du code similaire au précédent, consultez Attribut CORS avec [EnableCors] et Méthode RequireCors .

Activer CORS avec des attributs

L’activation de CORS avec l’attribut [EnableCors] et l’application d’une stratégie nommée uniquement aux points de terminaison qui nécessitent CORS fournissent le meilleur contrôle.

L’attribut [EnableCors] offre une alternative à l’application globale de CORS. L’attribut [EnableCors] active CORS pour les points de terminaison sélectionnés, plutôt que pour tous les points de terminaison :

  • [EnableCors] spécifie la stratégie par défaut.
  • [EnableCors("{Policy String}")] spécifie une stratégie nommée.

L’attribut [EnableCors] peut être appliqué à :

  • Razor Page PageModel
  • Contrôleur
  • Méthode d’action du contrôleur

Différentes stratégies peuvent être appliquées à des contrôleurs, des modèles de page ou des méthodes d’action avec l’attribut [EnableCors] . Lorsque l’attribut [EnableCors] est appliqué à un contrôleur, un modèle de page ou une méthode d’action et que CORS est activé dans l’intergiciel, les deux stratégies sont appliquées. Nous vous déconseillons de combiner des stratégies. Utilisez le [EnableCors]attribut ou middleware, pas les deux dans la même application.

Le code suivant applique une stratégie différente à chaque méthode :

[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
    // GET api/values
    [EnableCors("AnotherPolicy")]
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
        return new string[] { "green widget", "red widget" };
    }

    // GET api/values/5
    [EnableCors("Policy1")]
    [HttpGet("{id}")]
    public ActionResult<string> Get(int id)
    {
        return id switch
        {
            1 => "green widget",
            2 => "red widget",
            _ => NotFound(),
        };
    }
}

Le code suivant crée deux stratégies CORS :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("Policy1",
        policy =>
        {
            policy.WithOrigins("http://example.com",
                                "http://www.contoso.com");
        });

    options.AddPolicy("AnotherPolicy",
        policy =>
        {
            policy.WithOrigins("http://www.contoso.com")
                                .AllowAnyHeader()
                                .AllowAnyMethod();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseRouting();

app.UseCors();

app.UseAuthorization();

app.MapControllers();

app.Run();

Pour le meilleur contrôle de la limitation des requêtes CORS :

  • Utilisez [EnableCors("MyPolicy")] avec une stratégie nommée.
  • Ne définissez pas de stratégie par défaut.
  • N’utilisez pas le routage de point de terminaison.

Le code de la section suivante correspond à la liste précédente.

Consultez Test CORS pour obtenir des instructions sur le test du code similaire au code précédent.

Désactiver CORS

L’attribut [DisableCors] ne désactive pas CORS qui a été activé par le routage du point de terminaison.

Le code suivant définit la stratégie "MyPolicy"CORS :

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "MyPolicy",
        policy =>
        {
            policy.WithOrigins("http://example.com",
                                "http://www.contoso.com")
                    .WithMethods("PUT", "DELETE", "GET");
        });
});

builder.Services.AddControllers();
builder.Services.AddRazorPages();

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

app.UseEndpoints(endpoints => {
    endpoints.MapControllers();
    endpoints.MapRazorPages();
});

app.Run();

Le code suivant désactive CORS pour l’action GetValues2 :

[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public IActionResult Get() =>
        ControllerContext.MyDisplayRouteInfo();

    // GET api/values/5
    [HttpGet("{id}")]
    public IActionResult Get(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // PUT api/values/5
    [HttpPut("{id}")]
    public IActionResult Put(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);


    // GET: api/values/GetValues2
    [DisableCors]
    [HttpGet("{action}")]
    public IActionResult GetValues2() =>
        ControllerContext.MyDisplayRouteInfo();

}

Le code précédent :

Consultez Test CORS pour obtenir des instructions sur le test du code précédent.

Options de stratégie CORS

Cette section décrit les différentes options qui peuvent être définies dans une stratégie CORS :

AddPolicy est appelé dans Program.cs. Pour certaines options, il peut être utile de lire d’abord la section Fonctionnement de CORS .

Définir les origines autorisées

AllowAnyOrigin: autorise les requêtes CORS de toutes les origines avec n’importe quel schéma (http ou https). AllowAnyOrigin n’est pas sécurisé, car n’importe quel site web peut effectuer des demandes d’origine croisée à l’application.

Notes

La spécification de AllowAnyOrigin et AllowCredentials est une configuration non sécurisée et peut entraîner une falsification de requête intersites. Le service CORS retourne une réponse CORS non valide lorsqu’une application est configurée avec les deux méthodes.

AllowAnyOrigin affecte les demandes de contrôle préalable et l’en-tête Access-Control-Allow-Origin . Pour plus d’informations, consultez la section Demandes de contrôle préalable.

SetIsOriginAllowedToAllowWildcardSubdomains: définit la IsOriginAllowed propriété de la stratégie comme une fonction qui permet aux origines de correspondre à un domaine générique configuré lors de l’évaluation si l’origine est autorisée.

var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
        policy =>
        {
            policy.WithOrigins("https://*.example.com")
                .SetIsOriginAllowedToAllowWildcardSubdomains();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Définir les méthodes HTTP autorisées

AllowAnyMethod:

  • Autorise n’importe quelle méthode HTTP :
  • Affecte les demandes de contrôle préalable et l’en-tête Access-Control-Allow-Methods . Pour plus d’informations, consultez la section Demandes de contrôle préalable.

Définir les en-têtes de requête autorisés

Pour autoriser l’envoi d’en-têtes spécifiques dans une requête CORS, appelezWithHeaders et spécifiez les en-têtes autorisés :

using Microsoft.Net.Http.Headers;

var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
       policy =>
       {
           policy.WithOrigins("http://example.com")
                  .WithHeaders(HeaderNames.ContentType, "x-custom-header");
       });
});

builder.Services.AddControllers();

var app = builder.Build();

Pour autoriser tous les en-têtes de demande d’auteur, appelez AllowAnyHeader:

var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
        policy =>
        {
            policy.WithOrigins("https://*.example.com")
                   .AllowAnyHeader();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

AllowAnyHeader affecte les demandes préliminaires et l’en-tête Access-Control-Request-Headers . Pour plus d’informations, consultez la section Demandes de contrôle préalable.

Une correspondance de stratégie de middleware CORS à des en-têtes spécifiques spécifiés par WithHeaders n’est possible que lorsque les en-têtes envoyés correspondent Access-Control-Request-Headers exactement aux en-têtes indiqués dans WithHeaders.

Par exemple, considérez une application configurée comme suit :

app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));

CORS Middleware refuse une demande de contrôle préalable avec l’en-tête de requête suivant, car Content-Language (HeaderNames.ContentLanguage) n’est pas répertorié dans WithHeaders:

Access-Control-Request-Headers: Cache-Control, Content-Language

L’application retourne une réponse 200 OK , mais n’envoie pas les en-têtes CORS. Par conséquent, le navigateur ne tente pas la demande d’origine croisée.

Définir les en-têtes de réponse exposés

Par défaut, le navigateur n’expose pas tous les en-têtes de réponse à l’application. Pour plus d’informations, consultez Partage de ressources inter-origines W3C (terminologie) : en-tête de réponse simple.

Les en-têtes de réponse disponibles par défaut sont les suivants :

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

La spécification CORS appelle ces en-têtes de réponse simples. Pour rendre d’autres en-têtes disponibles pour l’application, appelez WithExposedHeaders:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MyExposeResponseHeadersPolicy",
        policy =>
        {
            policy.WithOrigins("https://*.example.com")
                   .WithExposedHeaders("x-custom-header");
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Informations d’identification dans les demandes inter-origines

Les informations d’identification nécessitent une gestion spéciale dans une requête CORS. Par défaut, le navigateur n’envoie pas d’informations d’identification avec une demande d’origine croisée. Les informations d’identification incluent des cookieschémas d’authentification s et HTTP. Pour envoyer des informations d’identification avec une demande d’origine croisée, le client doit définir XMLHttpRequest.withCredentials sur true.

Utilisation XMLHttpRequest directe :

var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;

Utilisation de jQuery :

$.ajax({
  type: 'get',
  url: 'https://www.example.com/api/test',
  xhrFields: {
    withCredentials: true
  }
});

Utilisation de l’API Fetch :

fetch('https://www.example.com/api/test', {
    credentials: 'include'
});

Le serveur doit autoriser les informations d’identification. Pour autoriser les informations d’identification inter-origines, appelez AllowCredentials:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MyMyAllowCredentialsPolicy",
        policy =>
        {
            policy.WithOrigins("http://example.com")
                   .AllowCredentials();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

La réponse HTTP comprend un Access-Control-Allow-Credentials en-tête, qui indique au navigateur que le serveur autorise les informations d’identification pour une demande d’origine croisée.

Si le navigateur envoie des informations d’identification mais que la réponse n’inclut pas d’en-tête valide Access-Control-Allow-Credentials , le navigateur n’expose pas la réponse à l’application et la demande d’origine croisée échoue.

L’autorisation des informations d’identification entre origines représente un risque de sécurité. Un site web d’un autre domaine peut envoyer les informations d’identification d’un utilisateur connecté à l’application pour le compte de l’utilisateur à l’insu de l’utilisateur.

La spécification CORS indique également que la définition des origines sur "*" (toutes les origines) n’est pas valide si l’en-tête Access-Control-Allow-Credentials est présent.

Demandes en pré-vol

Pour certaines requêtes CORS, le navigateur envoie une demande OPTIONS supplémentaire avant d’effectuer la demande réelle. Cette demande est appelée demande de contrôle préliminaire. Le navigateur peut ignorer la demande de pré-vol si toutes les conditions suivantes sont remplies :

  • La méthode de requête est GET, HEAD ou POST.
  • L’application ne définit pas d’en-têtes de requête autres que Accept, Accept-Language, Content-Language, Content-Typeou Last-Event-ID.
  • L’en-tête Content-Type , s’il est défini, a l’une des valeurs suivantes :
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

La règle sur les en-têtes de requête définis pour la demande cliente s’applique aux en-têtes que l’application définit en appelant setRequestHeader sur l’objet XMLHttpRequest . La spécification CORS appelle ces en-têtes des en-têtes de demande d’auteur. La règle ne s’applique pas aux en-têtes que le navigateur peut définir, tels que User-Agent, Hostou Content-Length.

Voici un exemple de réponse similaire à la demande de pré-vol effectuée à partir du bouton [Put test] dans la section Test CORS de ce document.

General:
Request URL: https://cors3.azurewebsites.net/api/values/5
Request Method: OPTIONS
Status Code: 204 No Content

Response Headers:
Access-Control-Allow-Methods: PUT,DELETE,GET
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f8...8;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Vary: Origin

Request Headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Method: PUT
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0

La demande de pré-vol utilise la méthode HTTP OPTIONS . Il peut inclure les en-têtes suivants :

Si la demande de préversion est refusée, l’application retourne une 200 OK réponse, mais ne définit pas les en-têtes CORS. Par conséquent, le navigateur ne tente pas la demande d’origine croisée. Pour obtenir un exemple de demande de pré-vol refusée, consultez la section Cors de test de ce document.

À l’aide des outils F12, l’application console affiche une erreur similaire à l’un des éléments suivants, en fonction du navigateur :

  • Firefox : Demande d’origine croisée bloquée : la même stratégie d’origine interdit la lecture de la ressource distante à l’adresse https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Raison : la demande CORS n’a pas abouti). En savoir plus
  • Chromium basé sur : L’accès à l’extraction à «https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 » à partir de l’origine «https://cors3.azurewebsites.net » a été bloqué par la stratégie CORS : la réponse à la demande de contrôle d’accès ne passe pas la vérification du contrôle d’accès : aucun en-tête « Access-Control-Allow-Origin » n’est présent sur la ressource demandée. Si une réponse opaque répond à vos besoins, définissez le mode de la requête sur « no-cors » pour extraire la ressource avec CORS désactivé.

Pour autoriser des en-têtes spécifiques, appelez WithHeaders:

using Microsoft.Net.Http.Headers;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MyAllowHeadersPolicy",
        policy =>
        {
        policy.WithOrigins("http://example.com")
                   .WithHeaders(HeaderNames.ContentType, "x-custom-header");
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Pour autoriser tous les en-têtes de demande d’auteur, appelez AllowAnyHeader:

using Microsoft.Net.Http.Headers;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MyAllowAllHeadersPolicy",
        policy =>
        {
            policy.WithOrigins("https://*.example.com")
                   .AllowAnyHeader();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Les navigateurs ne sont pas cohérents dans la façon dont ils définissent Access-Control-Request-Headers. Si :

  • Les en-têtes sont définis sur autre chose que "*"
  • AllowAnyHeader est appelé : incluez au moins Accept, Content-Typeet , ainsi Originque tous les en-têtes personnalisés que vous souhaitez prendre en charge.

Code de demande de contrôle préliminaire automatique

Lorsque la stratégie CORS est appliquée :

  • Globalement, en appelant app.UseCors dans Program.cs.
  • Utilisation de l’attribut [EnableCors] .

ASP.NET Core répond à la demande d’OPTIONS en pré-vol.

La section Test CORS de ce document illustre ce comportement.

Attribut [HttpOptions] pour les demandes de contrôle préalable

Lorsque CORS est activé avec la stratégie appropriée, ASP.NET Core répond généralement automatiquement aux demandes de contrôle préalable CORS.

Le code suivant utilise l’attribut [HttpOptions] pour créer des points de terminaison pour les demandes OPTIONS :

[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
    // OPTIONS: api/TodoItems2/5
    [HttpOptions("{id}")]
    public IActionResult PreflightRoute(int id)
    {
        return NoContent();
    }

    // OPTIONS: api/TodoItems2 
    [HttpOptions]
    public IActionResult PreflightRoute()
    {
        return NoContent();
    }

    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return BadRequest();
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

Consultez Tester CORS avec l’attribut [EnableCors] et la méthode RequireCors pour obtenir des instructions sur le test du code précédent.

Définir l’heure d’expiration du contrôle préliminaire

L’en-tête Access-Control-Max-Age spécifie la durée pendant laquelle la réponse à la demande de contrôle préliminaire peut être mise en cache. Pour définir cet en-tête, appelez SetPreflightMaxAge:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MySetPreflightExpirationPolicy",
        policy =>
        {
            policy.WithOrigins("http://example.com")
                   .SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Activer CORS sur un point de terminaison

Fonctionnement de CORS

Cette section décrit ce qui se passe dans une requête CORS au niveau des messages HTTP.

  • CORS n’est pas une fonctionnalité de sécurité. CORS est une norme W3C qui permet à un serveur d’assouplir la stratégie de même origine.
    • Par exemple, un acteur malveillant peut utiliser des scripts intersite (XSS) sur votre site et exécuter une demande intersite à son site compatible CORS pour voler des informations.
  • Une API n’est pas plus sûre en autorisant CORS.
    • Il appartient au client (navigateur) d’appliquer CORS. Le serveur exécute la requête et retourne la réponse. C’est le client qui retourne une erreur et bloque la réponse. Par exemple, l’un des outils suivants affiche la réponse du serveur :
  • Il s’agit d’un moyen pour un serveur d’autoriser les navigateurs à exécuter une requête d’APIXHR ou Fetch d’origine croisée qui, sinon, serait interdite.
    • Les navigateurs sans CORS ne peuvent pas effectuer de requêtes inter-origines. Avant CORS, JSONP était utilisé pour contourner cette restriction. JSONP n’utilise pas XHR, il utilise la <script> balise pour recevoir la réponse. Les scripts sont autorisés à être chargés entre les origines.

La spécification CORS a introduit plusieurs nouveaux en-têtes HTTP qui activent les requêtes d’origine croisée. Si un navigateur prend en charge CORS, il définit automatiquement ces en-têtes pour les demandes d’origine croisée. Le code JavaScript personnalisé n’est pas nécessaire pour activer CORS.

Bouton de test PUT sur l’exemple déployé

Voici un exemple de demande d’origine croisée du bouton de test Valeurs vers https://cors1.azurewebsites.net/api/values. En-tête Origin :

  • Fournit le domaine du site qui effectue la demande.
  • Est obligatoire et doit être différent de l’hôte.

En-têtes généraux

Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK

En-têtes de réponse

Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET

En-têtes de requête

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...

Dans OPTIONS les requêtes, le serveur définit l’en-tête d’en-têtesAccess-Control-Allow-Origin: {allowed origin} de réponse dans la réponse. Par exemple, l’exemple déployé de demande de bouton OPTIONSSupprimer [EnableCors] contient les en-têtes suivants :

En-têtes généraux

Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content

En-têtes de réponse

Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET

En-têtes de requête

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0

Dans les en-têtes de réponse précédents, le serveur définit l’en-tête Access-Control-Allow-Origin dans la réponse. La https://cors1.azurewebsites.net valeur de cet en-tête correspond à l’en-tête Origin de la demande.

Si AllowAnyOrigin est appelé, la Access-Control-Allow-Origin: *valeur générique est retournée. AllowAnyOrigin autorise n’importe quelle origine.

Si la réponse n’inclut pas l’en-tête Access-Control-Allow-Origin , la demande d’origine croisée échoue. Plus précisément, le navigateur interdit la demande. Même si le serveur retourne une réponse réussie, le navigateur ne rend pas la réponse disponible pour l’application cliente.

La redirection HTTP vers HTTPS entraîne des ERR_INVALID_REDIRECT sur la demande de contrôle préalable CORS

Les demandes adressées à un point de terminaison utilisant HTTP qui sont redirigées vers HTTPS par UseHttpsRedirection échec avec ERR_INVALID_REDIRECT on the CORS preflight request.

Les projets d’API peuvent rejeter les requêtes HTTP au lieu de les utiliser UseHttpsRedirection pour rediriger les requêtes vers HTTPS.

CORS dans IIS

Lors du déploiement sur IIS, CORS doit s’exécuter avant l’authentification Windows si le serveur n’est pas configuré pour autoriser l’accès anonyme. Pour prendre en charge ce scénario, le module IIS CORS doit être installé et configuré pour l’application.

Tester CORS

L’exemple de téléchargement contient du code pour tester CORS. Consultez Guide pratique pour télécharger. L’exemple est un projet d’API avec Razor Pages ajoutées :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "MyPolicy",
        policy =>
        {
            policy.WithOrigins("http://example.com",
                    "http://www.contoso.com",
                    "https://cors1.azurewebsites.net",
                    "https://cors3.azurewebsites.net",
                    "https://localhost:44398",
                    "https://localhost:5001")
                .WithMethods("PUT", "DELETE", "GET");
        });
});

builder.Services.AddControllers();
builder.Services.AddRazorPages();

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

app.MapControllers();
app.MapRazorPages();

app.Run();

Avertissement

WithOrigins("https://localhost:<port>"); doit uniquement être utilisé pour tester un exemple d’application similaire à l’exemple de code de téléchargement.

ValuesController Voici les points de terminaison pour les tests :

[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public IActionResult Get() =>
        ControllerContext.MyDisplayRouteInfo();

    // GET api/values/5
    [HttpGet("{id}")]
    public IActionResult Get(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // PUT api/values/5
    [HttpPut("{id}")]
    public IActionResult Put(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);


    // GET: api/values/GetValues2
    [DisableCors]
    [HttpGet("{action}")]
    public IActionResult GetValues2() =>
        ControllerContext.MyDisplayRouteInfo();

}

MyDisplayRouteInfo est fourni par le package NuGet Rick.Docs.Samples.RouteInfo et affiche des informations sur l’itinéraire.

Testez l’exemple de code précédent à l’aide de l’une des approches suivantes :

  • Utilisez l’exemple d’application déployé à l’adresse https://cors3.azurewebsites.net/. Il n’est pas nécessaire de télécharger l’exemple.
  • Exécutez l’exemple avec dotnet run à l’aide de l’URL par défaut de https://localhost:5001.
  • Exécutez l’exemple à partir de Visual Studio avec le port défini sur 44398 pour une URL de https://localhost:44398.

Utilisation d’un navigateur avec les outils F12 :

  • Sélectionnez le bouton Valeurs et passez en revue les en-têtes sous l’onglet Réseau .

  • Sélectionnez le bouton PUT test . Consultez Afficher les demandes OPTIONS pour obtenir des instructions sur l’affichage de la demande OPTIONS. Le test PUT crée deux requêtes, une demande de contrôle préliminaire OPTIONS et la demande PUT.

  • Sélectionnez le GetValues2 [DisableCors] bouton pour déclencher une demande CORS ayant échoué. Comme indiqué dans le document, la réponse retourne 200 réussites, mais la demande CORS n’est pas effectuée. Sélectionnez l’onglet Console pour voir l’erreur CORS. Selon le navigateur, une erreur semblable à ce qui suit s’affiche :

    L’accès à l’extraction à 'https://cors1.azurewebsites.net/api/values/GetValues2' partir de l’origine 'https://cors3.azurewebsites.net' a été bloqué par la stratégie CORS : aucun en-tête « Access-Control-Allow-Origin » n’est présent sur la ressource demandée. Si une réponse opaque répond à vos besoins, définissez le mode de la requête sur « no-cors » pour extraire la ressource avec CORS désactivé.

Les points de terminaison compatibles CORS peuvent être testés avec un outil, comme curl, Fiddler ou Postman. Lors de l’utilisation d’un outil, l’origine de la requête spécifiée par l’en-tête Origin doit différer de celle de l’hôte qui reçoit la demande. Si la requête n’est pas d’origine croisée basée sur la valeur de l’en-tête Origin :

  • Il n’est pas nécessaire d’utiliser CORS Middleware pour traiter la demande.
  • Les en-têtes CORS ne sont pas retournés dans la réponse.

La commande suivante utilise curl pour émettre une demande OPTIONS avec des informations :

curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i

Tester CORS avec l’attribut [EnableCors] et la méthode RequireCors

Considérez le code suivant qui utilise le routage de point de terminaison pour activer CORS sur une base par point de terminaison à l’aide RequireCorsde :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "MyPolicy",
        policy =>
        {
            policy.WithOrigins("http://example.com",
                    "http://www.contoso.com",
                    "https://cors1.azurewebsites.net",
                    "https://cors3.azurewebsites.net",
                    "https://localhost:44398",
                    "https://localhost:5001")
                .WithMethods("PUT", "DELETE", "GET");
        });
});

builder.Services.AddControllers();
builder.Services.AddRazorPages();

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapGet("/echo",
        context => context.Response.WriteAsync("echo"))
        .RequireCors("MyPolicy");

    endpoints.MapControllers();
    endpoints.MapRazorPages();
});

app.Run();

Notez que seul le point de /echo terminaison utilise le pour autoriser les RequireCors demandes d’origine croisée à l’aide de la stratégie spécifiée. Les contrôleurs ci-dessous activent CORS à l’aide de l’attribut [EnableCors].

Les éléments suivants TodoItems1Controller fournissent des points de terminaison pour les tests :

[Route("api/[controller]")]
[ApiController]
public class TodoItems1Controller : ControllerBase 
{
    // PUT: api/TodoItems1/5
    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id) {
        if (id < 1) {
            return Content($"ID = {id}");
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

    // Delete: api/TodoItems1/5
    [HttpDelete("{id}")]
    public IActionResult MyDelete(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // GET: api/TodoItems1
    [HttpGet]
    public IActionResult GetTodoItems() =>
        ControllerContext.MyDisplayRouteInfo();

    [EnableCors("MyPolicy")]
    [HttpGet("{action}")]
    public IActionResult GetTodoItems2() =>
        ControllerContext.MyDisplayRouteInfo();

    // Delete: api/TodoItems1/MyDelete2/5
    [EnableCors("MyPolicy")]
    [HttpDelete("{action}/{id}")]
    public IActionResult MyDelete2(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);
}

Testez le code précédent à partir de la page de test de l’exemple déployé.

Les boutons Supprimer [EnableCors] et GET [EnableCors] réussissent, car les points de terminaison ont [EnableCors] et répondent aux demandes de contrôle préalable. Les autres points de terminaison échouent. Le bouton GET échoue, car javaScript envoie :

 headers: {
      "Content-Type": "x-custom-header"
 },

Ce qui suit TodoItems2Controller fournit des points de terminaison similaires, mais inclut du code explicite pour répondre aux demandes OPTIONS :

[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
    // OPTIONS: api/TodoItems2/5
    [HttpOptions("{id}")]
    public IActionResult PreflightRoute(int id)
    {
        return NoContent();
    }

    // OPTIONS: api/TodoItems2 
    [HttpOptions]
    public IActionResult PreflightRoute()
    {
        return NoContent();
    }

    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return BadRequest();
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

    // [EnableCors] // Not needed as OPTIONS path provided.
    [HttpDelete("{id}")]
    public IActionResult MyDelete(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // [EnableCors] //  Warning ASP0023 Route '{id}' conflicts with another action route.
    //                  An HTTP request that matches multiple routes results in an ambiguous
    //                  match error.
    [EnableCors("MyPolicy")] // Required for this path.
    [HttpGet]
    public IActionResult GetTodoItems() =>
        ControllerContext.MyDisplayRouteInfo();

    [HttpGet("{action}")]
    public IActionResult GetTodoItems2() =>
        ControllerContext.MyDisplayRouteInfo();

    [EnableCors("MyPolicy")]  // Required for this path.
    [HttpDelete("{action}/{id}")]
    public IActionResult MyDelete2(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);
}

Testez le code précédent à partir de la page de test de l’exemple déployé. Dans la liste déroulante Contrôleur , sélectionnez Pré-vol , puis Définir le contrôleur. Tous les appels CORS aux points de TodoItems2Controller terminaison réussissent.

Ressources supplémentaires