Activer les requêtes Cross-Origin (CORS) dans ASP.NET Core

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’effectuer des demandes vers un autre domaine que 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 souhaiterez peut-être autoriser d’autres sites à effectuer des demandes Cross-Origin à votre application. Pour plus d’informations, consultez l' article Mozilla cors.

Partage des ressources Cross-Origin (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 Cross-Origin tout en rejetant d’autres.
  • Est plus sûr et plus flexible que les techniques antérieures, telles que JSONP.

Afficher ou télécharger 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 le même origine :

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

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

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

Activez CORS

Il existe trois façons d’activer CORS :

L’utilisation de l’attribut [EnableCors] avec une stratégie nommée offre un contrôle plus fin pour limiter les points de terminaison qui prennent en charge cors.

Avertissement

UseCors doit être appelé dans le bon ordre. Pour plus d’informations, consultez ordre des intergiciels (middleware). Par exemple, UseCors doit être appelé avant UseResponseCaching lors de l’utilisation de UseResponseCaching .

Chaque approche est décrite en détail dans les sections suivantes.

CORS avec la stratégie et l’intergiciel (middleware) nommés

L’intergiciel (middleware) CORS gère les demandes Cross-Origin. 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,
                              builder =>
                              {
                                  builder.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 :

  • Définit le nom de la stratégie sur _myAllowSpecificOrigins . Le nom de la stratégie est arbitraire.
  • Appelle la UseCors méthode d’extension et spécifie la _myAllowSpecificOrigins stratégie cors. UseCors Ajoute l’intergiciel (middleware) CORS. L’appel à UseCors doit être placé après UseRouting , mais avant UseAuthorization . Pour plus d’informations, consultez ordre des intergiciels (middleware).
  • Appelle AddCors avec une expression lambda. L’expression lambda prend un CorsPolicyBuilder objet. Les options de configuration, telles que WithOrigins , sont décrites plus loin dans cet article.
  • Active la _myAllowSpecificOrigins stratégie cors pour tous les points de terminaison de contrôleur. Consultez routage des points de terminaison pour appliquer une stratégie cors à des points de terminaison spécifiques.
  • Lors de l’utilisation de l’intergiciel (middleware) de mise en cache des réponses, appelez UseCors avant UseResponseCaching .

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

Consultez la page test cors pour obtenir des instructions sur le test de code similaire au code précédent.

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

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

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

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

Pour plus d’informations, consultez les options de stratégie cors dans ce document.

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

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy(MyAllowSpecificOrigins,
                          builder =>
                          {
                              builder.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 finale ( / ). Si l’URL se termine par / , la comparaison retourne false et aucun en-tête n’est retourné.

CORS avec la stratégie et l’intergiciel (middleware) par défaut

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

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddDefaultPolicy(
                builder =>
                {
                    builder.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 de contrôleur.

Activer cors avec routage du point de terminaison

L’activation de CORS sur une base par point de terminaison à l’aide de RequireCors ne prend pas en charge les demandes de prévols automatiques. Pour plus d’informations, consultez ce problème GitHub et test cors avec routage des points de terminaison et [HttpOptions].

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

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

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyAllowSpecificOrigins,
                              builder =>
                              {
                                  builder.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 l’intergiciel (middleware) CORS. Étant donné qu’une stratégie par défaut n’a pas été configurée, app.UseCors() seuls n’activent cors.
  • Les /echo points de terminaison et des contrôleurs autorisent les demandes Cross-Origin à l’aide de la stratégie spécifiée.
  • Les /echo2 Razor points de terminaison et pages n’autorisent pas les demandes Cross-Origin, 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 de point de terminaison avec RequireCors .

Consultez test cors avec routage des points de terminaison et [HttpOptions] pour obtenir des instructions sur le test de code similaire au précédent.

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 requièrent cors fournissent le contrôle le plus fin.

L’attribut [EnableCors] offre une alternative à l’application de cors globalement. L' [EnableCors] attribut Active cors pour les points de terminaison sélectionnés, plutôt que 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' [EnableCors] attribut peut être appliqué aux éléments suivants :

  • Razor Pagination 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' [EnableCors] attribut. Lorsque l' [EnableCors] attribut est appliqué à un contrôleur, à un modèle de page ou à une méthode d’action, et que cors est activé dans l’intergiciel (middleware), les deux stratégies sont appliquées. Nous vous recommandons de combiner les stratégies. Utilisez l' [EnableCors] attribut ou l’intergiciel (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",
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                                        "http://www.contoso.com");
                });

            options.AddPolicy("AnotherPolicy",
                builder =>
                {
                    builder.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 un meilleur contrôle de la limitation des demandes 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 la page test cors pour obtenir des instructions sur le test de 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 des points 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",
                builder =>
                {
                    builder.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' GetValues2 action :

[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 la page 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 Startup.ConfigureServices . Pour certaines options, il peut être utile de lire d’abord la section How cors Works .

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 un site Web peut effectuer des demandes Cross-Origin à l’application.

Notes

La spécification de AllowAnyOrigin et AllowCredentials de 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' Access-Control-Allow-Origin en-tête. Pour plus d’informations, consultez la section demandes préliminaires .

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

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

Définir les méthodes HTTP autorisées

AllowAnyMethod:

  • Autorise toute méthode HTTP :
  • Affecte les demandes préliminaires et l' Access-Control-Allow-Methods en-tête. Pour plus d’informations, consultez la section demandes préliminaires .

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

Pour autoriser l’envoi d’en-têtes spécifiques dans une demande CORS, appelée créer des en-têtes de demande, appelez WithHeaders et spécifiez les en-têtes autorisés :

options.AddPolicy("MyAllowHeadersPolicy",
    builder =>
    {
        // requires using Microsoft.Net.Http.Headers;
        builder.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",
    builder =>
    {
        builder.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 préliminaires .

Une stratégie d’intergiciel (middleware) CORS correspondant à des en-têtes spécifiques spécifiés par WithHeaders n’est possible que lorsque les en-têtes envoyés dans Access-Control-Request-Headers correspondent 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));

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

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

L’application renvoie une réponse 200 OK , mais n’envoie pas les en-têtes cors. Par conséquent, le navigateur n’essaie pas la demande 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 la page relative au partage des ressources Cross-Origin W3C (terminologie) : en-tête de réponse simple.

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

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

La spécification CORS appelle ces en -têtes de réponse simples en-têtes. Pour mettre d’autres en-têtes à la disposition de l’application, appelez WithExposedHeaders :

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

Informations d’identification dans les demandes Cross-Origin

Les informations d’identification nécessitent un traitement spécial dans une demande CORS. Par défaut, le navigateur n’envoie pas d’informations d’identification avec une demande Cross-Origin. Les informations d’identification incluent cookie les schémas d’authentification s et http. Pour envoyer des informations d’identification avec une demande Cross-Origin, le client doit affecter à la valeur XMLHttpRequest.withCredentials 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 Cross-Origin, appelez AllowCredentials :

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

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 Cross-Origin.

Si le navigateur envoie des informations d’identification mais que la réponse n’inclut pas d' Access-Control-Allow-Credentials en-tête valide, 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 dans un autre domaine peut envoyer les informations d’identification d’un utilisateur connecté à l’application pour le compte de l’utilisateur sans la connaissance de l’utilisateur.

La spécification CORS indique également que le paramètre Origins to "*" (All Origins) n’est pas valide si l' Access-Control-Allow-Credentials en-tête est présent.

Demandes préliminaires

Pour certaines demandes CORS, le navigateur envoie une demande d' options supplémentaire avant d’effectuer la demande réelle. Cette demande porte le nom de demande préliminaire. Le navigateur peut ignorer la demande préliminaire si toutes les conditions suivantes sont remplies :

  • La méthode de demande est : obtenir, début ou publication.
  • L’application ne définit pas les en-têtes de requête autres que Accept , Accept-Language ,, Content-Language Content-Type ou Last-Event-ID .
  • L' Content-Type en-tête, 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 demande définie pour la demande du client s’applique aux en-têtes définis par l’application en appelant setRequestHeader sur l' XMLHttpRequest objet. La spécification CORS appelle ces en-têtes créer des en-têtes de demande. La règle ne s’applique pas aux en-têtes que le navigateur peut définir, tels que User-Agent , Host ou Content-Length .

Voici un exemple de réponse similaire à la demande préliminaire effectuée à partir du bouton [placer un test] dans la section cors de test 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 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 n’essaie pas la demande 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 semblable à l’une des options 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 . (Raison : la demande CORS n’a pas pu être effectuée). En savoir plus
  • Basé sur le chrome : l’accès à la récupération sur « 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 préliminaire ne réussit 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",
    builder =>
    {
        // requires using Microsoft.Net.Http.Headers;
        builder.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",
    builder =>
    {
        builder.WithOrigins("https://*.example.com")
               .AllowAnyHeader();
    });

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

  • Les en-têtes sont définis sur une valeur autre que "*"
  • AllowAnyHeader est appelé : inclure 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 :

  • Globalement en appelant app.UseCors Startup.Configure .
  • À l’aide de l' [EnableCors] attribut.

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

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

La section « cors de test » 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 aux demandes préliminaires CORS automatiquement. Dans certains scénarios, cela peut ne pas être le cas. Par exemple, à l’aide de cors avec routage du point de terminaison.

Le code suivant utilise l’attribut [HttpOptions] pour créer des points de terminaison pour les demandes d’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 test cors avec routage des points de terminaison et [HttpOptions] pour obtenir des instructions sur le test du code précédent.

Définir le délai d’expiration du prévols

L' Access-Control-Max-Age en-tête 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",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
    });

Fonctionnement de CORS

Cette section décrit ce qui se produit dans une demande 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 inter-sites (XSS) sur votre site et exécuter une requête intersite vers son site Active cors pour voler des informations.
  • Une API n’est pas plus sûre en autorisant CORS.
    • C’est 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 :
  • C’est un moyen pour un serveur d’autoriser les navigateurs à exécuter une requête d’API XHR ou Fetch Cross-Origin qui serait sinon 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, elle utilise la <script> balise pour recevoir la réponse. Les scripts sont autorisés à être chargés sur plusieurs origines.

La spécification cors a introduit plusieurs nouveaux en-têtes HTTP qui permettent des demandes 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 demande Cross-Origin à partir du bouton de test des valeurs vers https://cors1.azurewebsites.net/api/values . L' Origin en-tête :

  • 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 demande

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 demandes, le serveur définit l’en-tête des en-têtes de réponse Access-Control-Allow-Origin: {allowed origin} dans la réponse. Par exemple, l' exemplede requête de bouton delete [EnableCors] déployé OPTIONS 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 demande

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' Origin en-tête de la demande.

Si AllowAnyOrigin est appelé, la Access-Control-Allow-Origin: * valeur de caractère générique est retournée. AllowAnyOrigin autorise toute origine.

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

Afficher les demandes OPTIONS

Par défaut, les navigateurs Chrome et Edge n’affichent pas les demandes 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 d’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 cors IIS 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 des Razor pages ajoutées :

public class StartupTest2
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: "MyPolicy",
                builder =>
                {
                    builder.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 semblable à l' exemple de code de téléchargement.

Les éléments suivants ValuesController fournissent les points de terminaison pour le 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 à l’aide de l’une des approches suivantes :

  • Utilisez l’exemple d’application déployée à l’adresse https://cors3.azurewebsites.net/ . Il n’est pas nécessaire de télécharger l’exemple.
  • Exécutez l’exemple avec l' dotnet run 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 .

À l’aide 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 d’options pour obtenir des instructions sur l’affichage de la demande d’options. Le test put crée deux demandes, une demande préliminaire options et la demande put.

  • Sélectionnez le GetValues2 [DisableCors] bouton pour déclencher une demande cors ayant échoué. Comme mentionné dans le document, la réponse retourne 200 réussite, mais la demande CORS n’est pas effectuée. Sélectionnez l’onglet console pour afficher l’erreur cors. En fonction du navigateur, une erreur semblable à la suivante 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 à l’aide d’un outil, tel que le terme « bouclé, Fiddler» ou « postal». Lors de l’utilisation d’un outil, l’origine de la demande spécifiée par l' Origin en-tête doit différer de l’hôte recevant la demande. Si la requête n’est pas une origine croisée en fonction de la valeur de l' Origin en-tête :

  • Il n’est pas nécessaire d’utiliser un intergiciel (middleware) CORS pour traiter la requête.
  • Les en-têtes CORS ne sont pas retournés dans la réponse.

La commande suivante utilise curl pour émettre une demande d’options avec des informations :

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

De test CORS avec routage du point de terminaison et [HttpOptions]

L’activation de CORS sur une base par point de terminaison à l’aide de RequireCors ne prend actuellement pas en charge les demandes de prévols automatiques. Prenons 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,
                builder =>
                {
                    builder.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 le test :

[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' exempledéployé.

Les boutons supprimer [EnableCors] et obtenir [EnableCors] fonctionnent correctement, car les points de terminaison ont [EnableCors] et répondent aux demandes préliminaires. Les autres points de terminaison échouent. Le bouton d' extraction échoue, car le code JavaScript envoie :

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

Les éléments suivants TodoItems2Controller fournissent des points de terminaison similaires, mais incluent du code explicite pour répondre aux demandes d’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évols , puis définir le contrôleur. Tous les appels CORS aux points de terminaison ont été TodoItems2Controller correctement effectués.

Ressources supplémentaires

Par Rick Anderson

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

La sécurité du navigateur empêche une page Web d’effectuer des demandes vers un autre domaine que 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 souhaiterez peut-être autoriser d’autres sites à effectuer des demandes Cross-Origin à votre application. Pour plus d’informations, consultez l' article Mozilla cors.

Partage des ressources Cross-Origin (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 Cross-Origin tout en rejetant d’autres.
  • Est plus sûr et plus flexible que les techniques antérieures, telles que JSONP.

Afficher ou télécharger 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 le même origine :

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

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

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

Internet Explorer ne prend pas en compte le port lors de la comparaison des origines.

CORS avec la stratégie et l’intergiciel (middleware) nommés

L’intergiciel (middleware) CORS gère les demandes Cross-Origin. Le code suivant active CORS pour l’application entière avec l’origine spécifiée :

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

    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public IConfiguration Configuration { get; }

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

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }

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

        app.UseCors(MyAllowSpecificOrigins); 

        app.UseHttpsRedirection();
        app.UseMvc();
    }
}

Le code précédent :

  • Définit le nom de la stratégie sur « _ myAllowSpecificOrigins ». Le nom de la stratégie est arbitraire.
  • Appelle la UseCors méthode d’extension, qui active cors.
  • Appelle AddCors avec une expression lambda. L’expression lambda prend un CorsPolicyBuilder objet. Les options de configuration, telles que WithOrigins , sont décrites plus loin dans cet article.

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

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

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

Pour plus d’informations, consultez les options de stratégie cors dans ce document.

La CorsPolicyBuilder méthode peut chaîner des méthodes, comme indiqué dans le code suivant :

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

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

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

Le code suivant applique des stratégies CORS à tous les points de terminaison des applications par le biais de l’intergiciel (middleware) CORS :

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

    app.UseCors();

    app.UseHttpsRedirection();
    app.UseMvc();
}

Remarque : UseCors doit être appelé avant UseMvc .

Consultez activer cors dans les Razor pages, les contrôleurs et les méthodes d’action pour appliquer la stratégie cors au niveau de la page/du contrôleur/action.

Consultez la page test cors pour obtenir des instructions sur le test de code similaire au code précédent.

Activer CORS avec des attributs

L’attribut [ EnableCors ] fournit une alternative à l’application de cors globalement. L' [EnableCors] attribut Active cors pour les points de terminaison sélectionnés, plutôt que tous les points de terminaison.

Utilisez [EnableCors] pour spécifier la stratégie par défaut et [EnableCors("{Policy String}")] spécifier une stratégie.

L' [EnableCors] attribut peut être appliqué aux éléments suivants :

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

Vous pouvez appliquer différentes stratégies à Controller/page-Model/action avec l' [EnableCors] attribut. Lorsque l' [EnableCors] attribut est appliqué à un modèle de contrôleurs/page/méthode d’action et que cors est activé dans l’intergiciel (middleware), les deux stratégies sont appliquées. Nous vous recommandons de ne pas combiner les stratégies. Utilisez l' [EnableCors] attribut ou l’intergiciel (middleware), pas les deux. Lorsque vous utilisez [EnableCors] , ne définissez pas de stratégie par défaut.

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]        // Default policy.
    [HttpGet("{id}")]
    public ActionResult<string> Get(int id)
    {
        switch (id)
        {
            case 1:
                return "green widget";
            case 2:
                return "red widget";
            default:
                return NotFound();
        }
    }
}

Le code suivant crée une stratégie CORS par défaut et une stratégie nommée "AnotherPolicy" :

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

    public IConfiguration Configuration { get; }

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

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

        });

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }

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

        app.UseHttpsRedirection();
        app.UseMvc();
    }
}

Désactiver CORS

L’attribut [ DISABLECORS ] désactive cors pour le contrôleur/page-Model/action.

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 How cors Works .

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 un site Web peut effectuer des demandes Cross-Origin à l’application.

Notes

La spécification de AllowAnyOrigin et AllowCredentials de est une configuration non sécurisée et peut entraîner une falsification de requête intersites. Pour une application sécurisée, spécifiez une liste exacte d’origines si le client doit s’autoriser à accéder aux ressources du serveur.

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

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

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

Définir les méthodes HTTP autorisées

AllowAnyMethod:

  • Autorise toute méthode HTTP :
  • Affecte les demandes préliminaires et l' Access-Control-Allow-Methods en-tête. Pour plus d’informations, consultez la section demandes préliminaires .

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

Pour autoriser l’envoi d’en-têtes spécifiques dans une demande CORS, appelée créer des en-têtes de demande, appelez WithHeaders et spécifiez les en-têtes autorisés :

options.AddPolicy("AllowHeaders",
    builder =>
    {
        builder.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("AllowAllHeaders",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .AllowAnyHeader();
    });

Ce paramètre affecte les demandes préliminaires et l' Access-Control-Request-Headers en-tête. Pour plus d’informations, consultez la section demandes préliminaires .

L’intergiciel (middleware) CORS autorise toujours l’envoi de quatre en-têtes dans le Access-Control-Request-Headers , quelles que soient les valeurs configurées dans les en-têtes CorsPolicy. Cette liste d’en-têtes comprend les éléments suivants :

  • Accept
  • Accept-Language
  • Content-Language
  • Origin

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

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

L’intergiciel (middleware) CORS répond correctement à une demande préliminaire avec l’en-tête de demande suivant, car Content-Language est toujours autorisé :

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

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 la page relative au partage des ressources Cross-Origin W3C (terminologie) : en-tête de réponse simple.

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

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

La spécification CORS appelle ces en -têtes de réponse simples en-têtes. Pour mettre d’autres en-têtes à la disposition de l’application, appelez WithExposedHeaders :

options.AddPolicy("ExposeResponseHeaders",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .WithExposedHeaders("x-custom-header");
    });

Informations d’identification dans les demandes Cross-Origin

Les informations d’identification nécessitent un traitement spécial dans une demande CORS. Par défaut, le navigateur n’envoie pas d’informations d’identification avec une demande Cross-Origin. Les informations d’identification incluent cookie les schémas d’authentification s et http. Pour envoyer des informations d’identification avec une demande Cross-Origin, le client doit affecter à la valeur XMLHttpRequest.withCredentials 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 Cross-Origin, appelez AllowCredentials :

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

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 Cross-Origin.

Si le navigateur envoie des informations d’identification mais que la réponse n’inclut pas d' Access-Control-Allow-Credentials en-tête valide, 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 dans un autre domaine peut envoyer les informations d’identification d’un utilisateur connecté à l’application pour le compte de l’utilisateur sans la connaissance de l’utilisateur.

La spécification CORS indique également que le paramètre Origins to "*" (All Origins) n’est pas valide si l' Access-Control-Allow-Credentials en-tête est présent.

Demandes préliminaires

Pour certaines demandes CORS, le navigateur envoie une demande supplémentaire avant d’effectuer la demande réelle. Cette demande porte le nom de demande préliminaire. Le navigateur peut ignorer la demande préliminaire si les conditions suivantes sont remplies :

  • La méthode de demande est : obtenir, début ou publication.
  • L’application ne définit pas les en-têtes de requête autres que Accept , Accept-Language ,, Content-Language Content-Type ou Last-Event-ID .
  • L' Content-Type en-tête, 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 demande définie pour la demande du client s’applique aux en-têtes définis par l’application en appelant setRequestHeader sur l' XMLHttpRequest objet. La spécification CORS appelle ces en-têtes créer des en- têtes de demande. La règle ne s’applique pas aux en-têtes que le navigateur peut définir, tels que User-Agent , Host ou Content-Length .

Voici un exemple de demande préliminaire :

OPTIONS https://myservice.azurewebsites.net/api/test HTTP/1.1
Accept: */*
Origin: https://myclient.azurewebsites.net
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: accept, x-my-custom-header
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Host: myservice.azurewebsites.net
Content-Length: 0

La requête de pré-vol utilise la méthode HTTP OPTIONS. Il comprend deux en-têtes spéciaux :

  • Access-Control-Request-Method: Méthode HTTP qui sera utilisée pour la demande réelle.
  • Access-Control-Request-Headers: Liste des en-têtes de requête que l’application définit sur la demande réelle. Comme indiqué précédemment, cela n’inclut pas les en-têtes définis par le navigateur, tels que User-Agent .

Lorsque CORS est activé avec la stratégie appropriée, ASP.NET Core répond généralement automatiquement aux demandes préliminaires CORS. Consultez l' attribut [HttpOptions] pour les demandes préliminaires.

Une demande préliminaire CORS peut inclure un Access-Control-Request-Headers en-tête, qui indique au serveur les en-têtes envoyés avec la demande réelle.

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

options.AddPolicy("AllowHeaders",
    builder =>
    {
        builder.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("AllowAllHeaders",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .AllowAnyHeader();
    });

Les navigateurs ne sont pas entièrement cohérents dans la façon dont ils sont définis Access-Control-Request-Headers . Si vous définissez des en-têtes autres que "*" (ou utilisez AllowAnyHeader ), vous devez inclure au moins Accept , Content-Type , et Origin , ainsi que tous les en-têtes personnalisés que vous souhaitez prendre en charge.

Voici un exemple de réponse à la demande préliminaire (en supposant que le serveur autorise la demande) :

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 0
Access-Control-Allow-Origin: https://myclient.azurewebsites.net
Access-Control-Allow-Headers: x-my-custom-header
Access-Control-Allow-Methods: PUT
Date: Wed, 20 May 2015 06:33:22 GMT

La réponse comprend un Access-Control-Allow-Methods en-tête qui répertorie les méthodes autorisées et éventuellement un Access-Control-Allow-Headers en-tête, qui répertorie les en-têtes autorisés. Si la demande préliminaire est réussie, le navigateur envoie la demande réelle.

Si la demande préliminaire est refusée, l’application renvoie une réponse 200 OK , mais n’envoie pas les en-têtes cors. Par conséquent, le navigateur n’essaie pas la demande Cross-Origin.

Définir le délai d’expiration du prévols

L' Access-Control-Max-Age en-tête 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("SetPreflightExpiration",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
    });

Fonctionnement de CORS

Cette section décrit ce qui se produit dans une demande 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 l’option empêcher les scripts inter-sites (XSS) sur votre site et exécuter une requête intersite vers son site Active cors pour voler des informations.
  • Votre API n’est pas plus sûre en autorisant CORS.
    • C’est 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 :
  • C’est un moyen pour un serveur d’autoriser les navigateurs à exécuter une requête d’API XHR ou Fetch Cross-Origin qui serait sinon interdite.
    • Les navigateurs (sans CORS) ne peuvent pas effectuer de demandes Cross-Origin. Avant CORS, JSONP était utilisé pour contourner cette restriction. JSONP n’utilise pas XHR, elle utilise la <script> balise pour recevoir la réponse. Les scripts sont autorisés à être chargés sur plusieurs origines.

La spécification cors a introduit plusieurs nouveaux en-têtes HTTP qui permettent des demandes 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.

Voici un exemple de demande Cross-Origin. L' Origin en-tête fournit le domaine du site qui effectue la demande. L' Origin en-tête est obligatoire et doit être différent de l’hôte.

GET https://myservice.azurewebsites.net/api/test HTTP/1.1
Referer: https://myclient.azurewebsites.net/
Accept: */*
Accept-Language: en-US
Origin: https://myclient.azurewebsites.net
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Host: myservice.azurewebsites.net

Si le serveur autorise la demande, il définit l' Access-Control-Allow-Origin en-tête dans la réponse. La valeur de cet en-tête correspond à l' Origin en-tête de la demande ou à la valeur du caractère générique "*" , ce qui signifie que toute origine est autorisée :

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/plain; charset=utf-8
Access-Control-Allow-Origin: https://myclient.azurewebsites.net
Date: Wed, 20 May 2015 06:27:30 GMT
Content-Length: 12

Test message

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

Tester CORS

Pour tester CORS :

  1. Créez un projet d’API. Vous pouvez également Télécharger l’exemple.
  2. Activez CORS à l’aide de l’une des approches décrites dans ce document. Par exemple :
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseHsts();
    }

    // Shows UseCors with CorsPolicyBuilder.
    app.UseCors(builder =>
    {
        builder.WithOrigins("http://example.com",
                            "http://www.contoso.com",
                            "https://localhost:44375",
                            "https://localhost:5001");
    });

    app.UseHttpsRedirection();
    app.UseMvc();
}

Avertissement

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

  1. Créez un projet d’application Web ( Razor pages ou Mvc). L’exemple utilise des Razor pages. Vous pouvez créer l’application Web dans la même solution que le projet d’API.
  2. Ajoutez le code en surbrillance suivant au fichier index. cshtml :
@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="text-center">
    <h1 class="display-4">CORS Test</h1>
</div>

<div>
    <input type="button" value="Test" 
           onclick="requestVal('https://<web app>.azurewebsites.net/api/values')" />
    <span id='result'></span>
</div>

<script>
    function requestVal(uri) {
        const resultSpan = document.getElementById('result');

        fetch(uri)
            .then(response => response.json())
            .then(data => resultSpan.innerText = data)
            .catch(error => resultSpan.innerText = 'See F12 Console for error');
    }
</script>
  1. Dans le code précédent, remplacez url: 'https://<web app>.azurewebsites.net/api/values/1', par l’URL de l’application déployée.

  2. Déployez le projet API. Par exemple, déployez sur Azure.

  3. Exécutez les Razor pages ou l’application MVC à partir du bureau, puis cliquez sur le bouton test . Utilisez les outils F12 pour passer en revue les messages d’erreur.

  4. Supprimez l’origine localhost de WithOrigins et déployez l’application. Vous pouvez également exécuter l’application cliente avec un autre port. Par exemple, exécutez à partir de Visual Studio.

  5. Testez avec l’application cliente. Les échecs CORS retournent une erreur, mais le message d’erreur n’est pas disponible pour JavaScript. Utilisez l’onglet Console des outils F12 pour afficher l’erreur. En fonction du navigateur, vous recevez une erreur (dans la console outils F12) semblable à ce qui suit :

    • Utilisation de Microsoft Edge :

      SEC7120 : [CORS] l’origine https://localhost:44375 n’a pas été trouvée https://localhost:44375 dans l’en-tête de réponse Access-Control-allow-Origin pour la ressource Cross-Origin à l’adresse https://webapi.azurewebsites.net/api/values/1

    • Utilisation de chrome :

      L’accès à XMLHttpRequest à https://webapi.azurewebsites.net/api/values/1 partir de l’origine https://localhost:44375 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.

Les points de terminaison compatibles CORS peuvent être testés à l’aide d’un outil tel que Fiddler ou postal. Lors de l’utilisation d’un outil, l’origine de la demande spécifiée par l' Origin en-tête doit différer de l’hôte recevant la demande. Si la requête n’est pas une origine croisée en fonction de la valeur de l' Origin en-tête :

  • Il n’est pas nécessaire d’utiliser un intergiciel (middleware) CORS pour traiter la requête.
  • Les en-têtes CORS ne sont pas retournés dans la réponse.

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 cors IIS doit être installé et configuré pour l’application.

Ressources supplémentaires