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.
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.
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érenthttps://www.example.com/foo.html
: Sous-domaine différenthttp://example.com/foo.html
: Schéma différenthttps://example.com:9000/foo.html
: Port différent
Activez CORS
Il existe trois façons d’activer CORS :
- Dans le middleware à l’aide d’une stratégie nommée ou d’une stratégie par défaut.
- Utilisation du routage de point de terminaison.
- Avec l’attribut [EnableCors].
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 :
- Définit le nom de la stratégie sur
_myAllowSpecificOrigins
. Le nom de la stratégie est arbitraire. - Appelle la méthode d’extension UseCors et spécifie la
_myAllowSpecificOrigins
stratégie CORS.UseCors
ajoute le middleware CORS. L’appel àUseCors
doit être placé aprèsUseRouting
, mais avantUseAuthorization
. Pour plus d’informations, consultez Ordre des intergiciels. - Appelle AddCors avec une expression lambda. L’lambda prend un CorsPolicyBuilder objet. Les options de configuration, telles que
WithOrigins
, sont décrites plus loin dans cet article. - Active la stratégie
_myAllowSpecificOrigins
CORS pour tous les points de terminaison de contrôleur. Consultez Routage de point de terminaison pour appliquer une stratégie CORS à des points de terminaison spécifiques. - Lorsque vous utilisez l’intergiciel de mise en cache de réponse, appelez UseCors avant UseResponseCaching.
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 :
- N’active pas CORS avec le routage de point de terminaison.
- Ne définit pas de stratégie CORS par défaut.
- Utilise [EnableCors(« MyPolicy »)] pour activer la
"MyPolicy"
stratégie CORS pour le contrôleur. - Désactive CORS pour la
GetValues2
méthode .
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 :
- Définir les origines autorisées
- Définir les méthodes HTTP autorisées
- Définir les en-têtes de requête autorisés
- Définir les en-têtes de réponse exposés
- Informations d’identification dans les demandes cross-origin
- Définir l’heure d’expiration de la préversion
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
- 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-Type
ouLast-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
, Host
ou 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 :
- Access-Control-Request-Method : méthode HTTP qui sera utilisée pour la requête réelle.
- Access-Control-Request-Headers : liste d’en-têtes de requête que l’application définit sur la requête réelle. Comme indiqué précédemment, cela n’inclut pas les en-têtes que le navigateur définit, tels que
User-Agent
. - Access-Control-Allow-Methods
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
, etOrigin
, 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
dansProgram.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 :
- Fiddler
- Postman
- HttpClient .NET
- Un navigateur web en entrant l’URL dans la barre d’adresse.
- 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.
- 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
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 OPTIONS
Supprimer [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 dehttps://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 RequireCors
de :
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érenthttps://www.example.com/foo.html
: Sous-domaine différenthttp://example.com/foo.html
: Schéma différenthttps://example.com:9000/foo.html
: Port différent
Activez CORS
Il existe trois façons d’activer CORS :
- Dans le middleware à l’aide d’une stratégie nommée ou d’une stratégie par défaut.
- Utilisation du routage de point de terminaison.
- Avec l’attribut [EnableCors].
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 :
- Définit le nom de la stratégie sur
_myAllowSpecificOrigins
. Le nom de la stratégie est arbitraire. - Appelle la méthode d’extension UseCors et spécifie la
_myAllowSpecificOrigins
stratégie CORS.UseCors
ajoute le middleware CORS. L’appel àUseCors
doit être placé aprèsUseRouting
, mais avantUseAuthorization
. Pour plus d’informations, consultez Ordre des intergiciels. - Appelle AddCors avec une expression lambda. L’lambda prend un CorsPolicyBuilder objet. Les options de configuration, telles que
WithOrigins
, sont décrites plus loin dans cet article. - Active la stratégie
_myAllowSpecificOrigins
CORS pour tous les points de terminaison de contrôleur. Consultez Routage de point de terminaison pour appliquer une stratégie CORS à des points de terminaison spécifiques. - Lorsque vous utilisez l’intergiciel de mise en cache de réponse, appelez UseCors avant UseResponseCaching.
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 RequireCors
de 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 :
- N’active pas CORS avec le routage de point de terminaison.
- Ne définit pas de stratégie CORS par défaut.
- Utilise [EnableCors(« MyPolicy »)] pour activer la
"MyPolicy"
stratégie CORS pour le contrôleur. - Désactive CORS pour la
GetValues2
méthode .
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 :
- Définir les origines autorisées
- Définir les méthodes HTTP autorisées
- Définir les en-têtes de requête autorisés
- Définir les en-têtes de réponse exposés
- Informations d’identification dans les demandes inter-origines
- Définir l’heure d’expiration du contrôle préliminaire
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
- 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-Type
ouLast-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
, Host
ou 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 :
- Access-Control-Request-Method : méthode HTTP qui sera utilisée pour la requête réelle.
- Access-Control-Request-Headers : liste d’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 d’en-têtes que le navigateur définit, tels que
User-Agent
. - Access-Control-Allow-Methods
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-Type
et , ainsiOrigin
que 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
dansProgram.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 :
- Fiddler
- Postman
- HttpClient .NET
- Un navigateur web en entrant l’URL dans la barre d’adresses.
- 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.
- 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
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 OPTIONS
Supprimer [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
ouedge://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 dehttps://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érenthttps://www.example.com/foo.html
: Sous-domaine différenthttp://example.com/foo.html
: Schéma différenthttps://example.com:9000/foo.html
: Port différent
Activez CORS
Il existe trois façons d’activer CORS :
- Dans le middleware à l’aide d’une stratégie nommée ou d’une stratégie par défaut.
- Utilisation du routage de point de terminaison.
- Avec l’attribut [EnableCors].
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 :
- Définit le nom de la stratégie sur
_myAllowSpecificOrigins
. Le nom de la stratégie est arbitraire. - Appelle la méthode d’extension UseCors et spécifie la
_myAllowSpecificOrigins
stratégie CORS.UseCors
ajoute le middleware CORS. L’appel àUseCors
doit être placé aprèsUseRouting
, mais avantUseAuthorization
. Pour plus d’informations, consultez Ordre des intergiciels. - Appelle AddCors avec une expression lambda. L’lambda prend un CorsPolicyBuilder objet. Les options de configuration, telles que
WithOrigins
, sont décrites plus loin dans cet article. - Active la stratégie
_myAllowSpecificOrigins
CORS pour tous les points de terminaison de contrôleur. Consultez Routage de point de terminaison pour appliquer une stratégie CORS à des points de terminaison spécifiques. - Lorsque vous utilisez l’intergiciel de mise en cache de réponse, appelez UseCors avant UseResponseCaching.
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 RequireCors
de 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 :
- N’active pas CORS avec le routage de point de terminaison.
- Ne définit pas de stratégie CORS par défaut.
- Utilise [EnableCors(« MyPolicy »)] pour activer la
"MyPolicy"
stratégie CORS pour le contrôleur. - Désactive CORS pour la
GetValues2
méthode .
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 :
- Définir les origines autorisées
- Définir les méthodes HTTP autorisées
- Définir les en-têtes de requête autorisés
- Définir les en-têtes de réponse exposés
- Informations d’identification dans les demandes cross-origin
- Définir l’heure d’expiration de la préversion
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
- 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-Type
ouLast-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
, Host
ou 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 :
- Access-Control-Request-Method : méthode HTTP qui sera utilisée pour la requête réelle.
- Access-Control-Request-Headers : liste d’en-têtes de requête que l’application définit sur la requête réelle. Comme indiqué précédemment, cela n’inclut pas les en-têtes que le navigateur définit, tels que
User-Agent
. - Access-Control-Allow-Methods
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
, etOrigin
, 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
dansStartup.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 :
- Fiddler
- Postman
- HttpClient .NET
- Un navigateur web en entrant l’URL dans la barre d’adresse.
- 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.
- 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
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 OPTIONS
Supprimer [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
ouedge://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 dehttps://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érenthttps://www.example.com/foo.html
: Sous-domaine différenthttp://example.com/foo.html
: Schéma différenthttps://example.com:9000/foo.html
: Port différent
Activez CORS
Il existe trois façons d’activer CORS :
- Dans le middleware à l’aide d’une stratégie nommée ou d’une stratégie par défaut.
- Utilisation du routage de point de terminaison.
- Avec l’attribut [EnableCors].
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 :
- Définit le nom de la stratégie sur
_myAllowSpecificOrigins
. Le nom de la stratégie est arbitraire. - Appelle la méthode d’extension UseCors et spécifie la
_myAllowSpecificOrigins
stratégie CORS.UseCors
ajoute le middleware CORS. L’appel àUseCors
doit être placé aprèsUseRouting
, mais avantUseAuthorization
. Pour plus d’informations, consultez Ordre des intergiciels. - Appelle AddCors avec une expression lambda. L’lambda prend un CorsPolicyBuilder objet. Les options de configuration, telles que
WithOrigins
, sont décrites plus loin dans cet article. - Active la stratégie
_myAllowSpecificOrigins
CORS pour tous les points de terminaison de contrôleur. Consultez Routage de point de terminaison pour appliquer une stratégie CORS à des points de terminaison spécifiques. - Lorsque vous utilisez l’intergiciel de mise en cache de réponse, appelez UseCors avant UseResponseCaching.
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 :
- N’active pas CORS avec le routage de point de terminaison.
- Ne définit pas de stratégie CORS par défaut.
- Utilise [EnableCors(« MyPolicy »)] pour activer la
"MyPolicy"
stratégie CORS pour le contrôleur. - Désactive CORS pour la
GetValues2
méthode .
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 :
- Définir les origines autorisées
- Définir les méthodes HTTP autorisées
- Définir les en-têtes de requête autorisés
- Définir les en-têtes de réponse exposés
- Informations d’identification dans les demandes inter-origines
- Définir l’heure d’expiration du contrôle préliminaire
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
- 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-Type
ouLast-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
, Host
ou 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 :
- Access-Control-Request-Method : méthode HTTP qui sera utilisée pour la requête réelle.
- Access-Control-Request-Headers : liste d’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 d’en-têtes que le navigateur définit, tels que
User-Agent
. - Access-Control-Allow-Methods
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-Type
et , ainsiOrigin
que 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
dansProgram.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 :
- Fiddler
- Postman
- HttpClient .NET
- Un navigateur web en entrant l’URL dans la barre d’adresses.
- 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.
- 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
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 OPTIONS
Supprimer [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 dehttps://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 RequireCors
de :
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.