Habilitar solicitudes entre orígenes (CORS) en ASP.NET Core
Nota:
Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 9 de este artículo.
Advertencia
Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulte la directiva de compatibilidad de .NET y .NET Core. Para la versión actual, consulte la versión de .NET 9 de este artículo.
Importante
Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.
Para la versión actual, consulte la versión de .NET 9 de este artículo.
Por Rick Anderson y Kirk Larkin
En este artículo se muestra cómo Cross-Origin Resource Sharing (CORS o Uso compartido de recursos entre orígenes) se habilita en una aplicación ASP.NET Core.
La seguridad del explorador evita que una página web realice solicitudes a un dominio diferente del que atendió a dicha página web. Esta restricción se denomina directiva de mismo origen. La directiva de mismo origen evita que un sitio malintencionado lea información confidencial de otro sitio. A veces, es posible que quiera permitir que otros sitios realicen solicitudes entre orígenes a la aplicación. Para obtener más información, consulte el artículo CORS de Mozilla.
Intercambio de recursos de origen cruzado (CORS):
- Es una norma de W3C que permite mayor flexibilidad a los servidores en la directiva de mismo origen.
- No es una característica de seguridad, CORS relaja la seguridad. Una API no es más segura al permitir CORS. Para más información, vea Funcionamiento de CORS.
- Permite explícitamente a un servidor a permitir algunas solicitudes de origen cruzado y rechazar otras.
- Es más seguro y flexible que las técnicas anteriores, como JSONP.
Vea o descargue el código de ejemplo (cómo descargarlo)
Mismo origen
Dos URL tienen el mismo origen si tienen esquemas, hosts y puertos idénticos (RFC 6454).
Estas dos direcciones URL tienen el mismo origen:
https://example.com/foo.html
https://example.com/bar.html
Estas direcciones URL tienen orígenes diferentes a las dos direcciones URL anteriores:
https://example.net
: Dominio diferentehttps://contoso.example.com/foo.html
: Subdominio diferentehttp://example.com/foo.html
: Esquema diferentehttps://example.com:9000/foo.html
: Puerto diferente
Habilitación de CORS
Existen tres formas de habilitar CORS:
- En middleware mediante una directiva con nombre o directiva predeterminada .
- Utilizando el enrutamiento de punto de conexión.
- Con el atributo [EnableCors].
El uso del atributo [EnableCors] con una directiva con nombre proporciona el mejor control para limitar los puntos finales que admiten CORS.
Advertencia
UseCors debe llamarse en el orden correcto. Para obtener más información vea Orden de Middleware. Por ejemplo, UseCors
debe llamarse antes de UseResponseCaching cuando se utiliza UseResponseCaching
.
Cada ennfoque se detalla en las secciones siguientes.
CORS con directivas con nombre y middleware
El middleware de CORS controla las solicitudes entre orígenes. El código siguiente aplica una directiva de CORS a todos los puntos de conexión de la aplicación con los orígenes especificados:
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();
El código anterior:
- Establece el nombre de la directiva en
_myAllowSpecificOrigins
. El nombre de la política es arbitrario. - Llama al UseCorsmétodo de extensión y especifica la
_myAllowSpecificOrigins
directiva CORS.UseCors
agrega el middleware CORS. La llamada aUseCors
debe colocarse después deUseRouting
, pero antes deUseAuthorization
. Para obtener más información vea Orden de Middleware. - Llama a AddCors con una expresión lambda . La lambda toma un objeto CorsPolicyBuilder. Las opciones de configuración, como
WithOrigins
, se describen más adelante en este artículo. - Habilita la
_myAllowSpecificOrigins
directiva de CORS para todos los puntos de conexión del controlador. Consulte enrutamiento de punto final para aplicar una directiva CORS a puntos finales específicos. - Al usar middleware de almacenamiento en caché de respuesta, llame UseCors antes de UseResponseCaching.
Con el enrutamiento de puntos de conexión, el middleware de CORS debe configurarse para ejecutarse entre las llamadas a UseRouting
y UseEndpoints
.
La AddCors llamada al método añade servicios CORS al contenedor de servicios de la aplicación:
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();
Para obtener más información, consulte Opciones de directiva CORS en este documento.
Los CorsPolicyBuilder métodos se pueden encadenar, como se muestra en el código siguiente:
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();
Nota: La URL especificada no debe contener una barra diagonal (/
). Si la dirección URL finaliza con /
, la comparación devuelve false
y no se devuelve ningún encabezado.
Orden de UseCors y UseStaticFiles
Normalmente, UseStaticFiles
se llama a antes de UseCors
. Las aplicaciones que usan JavaScript para recuperar archivos estáticos entre sitios deben llamar a UseCors
antes de UseStaticFiles
.
CORS con directivas predeterminadas y middleware
El código resaltado siguiente habilita la directiva CORS predeterminada:
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();
El código anterior aplica la directiva CORS predeterminada a todos los puntos de conexión del controlador.
Habilitación de CORS con enrutamiento de punto de conexión
Con el enrutamiento de punto final, CORS se puede habilitar por punto final utilizando el RequireCors conjunto de métodos de extensión:
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();
En el código anterior:
app.UseCors
agrega el middleware CORS. Debido a que no se ha configurado una política predeterminada,app.UseCors()
por sí solo no habilita CORS.- Los
/echo
y puntos de conexión del controlador permiten solicitudes entre orígenes mediante la directiva especificada. - Los
/echo2
y Razor puntos de conexión de páginas no permiten solicitudes de origen cruzado porque no se especificó ninguna política predeterminada.
El [DisableCors]atributo no deshabilita el CORS que ha sido habilitado por el enrutamiento de punto final con RequireCors
.
Consulte Probar CORS con el atributo [EnableCors] y el método RequireCors para obtener instrucciones sobre cómo probar código similar al anterior.
Habilitación de CORS con atributos
Habilitar CORS con el atributo [EnableCors] y aplicar una directiva con nombre solo a los puntos de conexión que requieren CORS proporcionan el mejor control.
El atributo [EnableCors] proporciona una alternativa a aplicar CORS globalmente. El [EnableCors]
atributo habilita CORS para los puntos de conexión seleccionados, en lugar de todos los puntos de conexión:
[EnableCors]
especifica la política predeterminada.[EnableCors("{Policy String}")]
especifica una directiva guardada.
El atributo [EnableCors]
se puede aplicar a:
- Razor Página
PageModel
- Controlador
- Método de acción del controlador
Se pueden aplicar diferentes directivas a controladores, modelos de página o métodos de acción con el atributo [EnableCors]
. Cuando el atributo [EnableCors]
se aplica a un controlador, modelo de página o método de acción, y CORS está habilitado en middleware, se aplican ambas directivas. Se recomienda combinar directivas. Use el el atributo o middleware [EnableCors]
, no ambos, en la misma aplicación.
El código siguiente aplica una directiva diferente a cada método:
[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(),
};
}
}
El siguiente código crea dos políticas 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();
Para obtener el mejor control de la limitación de solicitudes CORS:
- Use
[EnableCors("MyPolicy")]
con una directiva con nombre. - No defina una directiva predeterminada.
- No utilice enrutamiento de punto final .
El código de la sección siguiente cumple la lista anterior.
Deshabilitación de CORS
El atributo [DisableCors]no deshabilita el CORS que ha sido habilitado por el enrutamiento de punto final .
El siguiente código define la política 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();
El código siguiente deshabilita CORS para la GetValues2
acción:
[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();
}
El código anterior:
- No habilita CORS con enrutamiento de punto final .
- No define una directiva CORS predeterminada.
- Usa [EnableCors("MyPolicy")] para habilitar la
"MyPolicy"
directiva CORS para el controlador. - Deshabilita CORS para el
GetValues2
método .
Consulte Probar CORS para obtener instrucciones sobre cómo probar el código anterior.
Opciones de directiva CORS
En esta sección se describen las distintas opciones que se pueden establecer en una directiva de CORS:
- Establecimiento de los orígenes permitidos
- Establecer los métodos HTTP permitidos
- Establecer los encabezados de solicitud permitidos
- Establecer los encabezados de respuesta expuestos
- Credenciales en solicitudes entre orígenes
- Establecer la hora de expiración previa
AddPolicy es llamado por Program.cs
. Para algunas opciones, puede resultar útil leer primero la sección Funcionamiento de CORS .
Establecimiento de los orígenes permitidos
AllowAnyOrigin: Permite solicitudes CORS desde todos los orígenes con cualquier esquema (http
o https
). AllowAnyOrigin
no es seguro porque cualquier sitio web puede realizar solicitudes entre orígenes a la aplicación.
Nota
Especificar AllowAnyOrigin
y AllowCredentials
es una configuración no segura y puede dar lugar a una falsificación de solicitud entre sitios. El servicio CORS devuelve una respuesta CORS no válida cuando una aplicación está configurada con ambos métodos.
AllowAnyOrigin
afecta a las solicitudes preparatorias y al Access-Control-Allow-Origin
encabezado. Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
SetIsOriginAllowedToAllowWildcardSubdomains: establece la IsOriginAllowed propiedad de la directiva en una función que permite que los orígenes coincidan con un dominio comodín configurado al evaluar si se permite el origen.
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();
Establecimiento de los métodos HTTP permitidos
- Permite cualquier método HTTP:
- Afecta a las solicitudes preparatorias y al encabezado
Access-Control-Allow-Methods
. Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
Establecimiento de los encabezados de solicitud permitidos
Para permitir que se envíen encabezados específicos en una solicitud CORS, llamada encabezados de solicitud de autor , llame a WithHeaders y especifique los encabezados permitidos:
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();
Para permitir todos los encabezados de solicitud de autor, llama a 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
afecta a las solicitudes de preventa y al encabezado Access-Control-Request-Headers . Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
Una coincidencia de política de CORS Middleware con encabezados específicos especificados por WithHeaders
solo es posible cuando los encabezados enviados en Access-Control-Request-Headers
coinciden exactamente con los encabezados indicados en WithHeaders
.
Por ejemplo, considere la posibilidad de configurar una aplicación como se indica a continuación:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
CORS Middleware rechaza la solicitud preparatoria con el siguiente encabezado de solicitud porque Content-Language
(HeaderNames.ContentLanguage ) no aparece en WithHeaders
:
Access-Control-Request-Headers: Cache-Control, Content-Language
La aplicación devuelve una respuesta 200 OK pero no devuelve los encabezados CORS. Por lo tanto, el explorador no intenta la solicitud entre orígenes.
Establecer los encabezados de respuesta expuestos
De forma predeterminada, el explorador no expone todos los encabezados de respuesta a la aplicación. Para obtener más información, consulte W3C Cross-Origin Resource Sharing (Terminology): Simple Response Header.
Los encabezados de respuesta que están disponibles de forma predeterminada son:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
La especificación CORS llama a estos encabezados de respuesta simples. Para que otros encabezados estén disponibles para la aplicación, llame a 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();
Credenciales en solicitudes entre orígenes
Las credenciales requieren un control especial en una solicitud CORS. De forma predeterminada, el navegador no envía credenciales con una solicitud de origen cruzado. Las credenciales incluyen cookies y esquemas de autenticación HTTP. Para enviar credenciales con una solicitud de origen cruzado, el cliente debe establecer XMLHttpRequest.withCredentials
en true
.
Uso XMLHttpRequest
directo:
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
Uso de jQuery:
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
Uso de la Fetch API:
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
El servidor debe permitir las credenciales. Para permitir credenciales de origen cruzado, llame a 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 respuesta HTTP incluye un encabezado Access-Control-Allow-Credentials
, que le dice al navegador que el servidor permite credenciales para una solicitud de origen cruzado.
Si el explorador envía credenciales, pero la respuesta no incluye un encabezado válido Access-Control-Allow-Credentials
, el explorador no expone la respuesta a la aplicación y se produce un error en la solicitud entre orígenes.
Permitir credenciales entre orígenes es un riesgo de seguridad. Un sitio web de otro dominio puede enviar las credenciales de un usuario que ha iniciado sesión a la aplicación en nombre del usuario sin el conocimiento del usuario.
La especificación CORS también indica que establecer orígenes en "*"
(todos los orígenes) no es válido si el Access-Control-Allow-Credentials
encabezado está presente.
Solicitud preparatoria
Para algunas solicitudes CORS, el navegador envía una solicitud adicional de OPTIONS antes de realizar la solicitud real. Esta solicitud se denomina solicitud preparatoria. El navegador puede omitir la solicitud preparatoria si se cumplen todas las siguientes condiciones:
- El método de solicitud es GET, HE0AD o POST.
- La aplicación no establece encabezados de solicitud que no sean
Accept
,Accept-Language
,Content-Language
,Content-Type
oLast-Event-ID
. - El encabezado
Content-Type
, si está configurado, tiene uno de los siguientes valores:application/x-www-form-urlencoded
multipart/form-data
text/plain
La regla sobre encabezados de solicitud establecida para la solicitud del cliente se aplica a los encabezados que la aplicación establece llamando a setRequestHeader
en el objeto XMLHttpRequest
. La especificación CORS llama a estos encabezados de solicitud de autor. La regla no se aplica a los encabezados que el explorador puede establecer, como User-Agent
, Host
o Content-Length
.
Nota:
En este artículo se incluyen las direcciones URL creadas al implementar el código de ejemplo en dos sitios web de Azure, https://cors3.azurewebsites.net
y https://cors.azurewebsites.net
.
La siguiente es una respuesta de ejemplo similar a la solicitud preparatoria realizada desde el botón [Put test] en la sección Test CORS de este documento.
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 solicitud preparatoria utiliza el método OPCIONES HTTP . Puede incluir los siguientes encabezados:
- Access-Control-Request-Method: método HTTP que se usará para la solicitud real.
- Access-Control-Request-Headers: una lista de encabezados de solicitud que la aplicación establece en la solicitud real. Como se indicó anteriormente, esto no incluye encabezados que establece el explorador, como
User-Agent
.
Si se rechaza la solicitud de preventa, la aplicación devuelve una respuesta 200 OK
pero no establece los encabezados CORS. Por lo tanto, el explorador no intenta la solicitud entre orígenes. Para obtener un ejemplo de una solicitud preparatoria denegada, consulte la sección Probar CORS de este documento.
Con las herramientas F12, la aplicación de consola muestra un error similar a uno de los siguientes, según el explorador:
- Firefox: solicitud de origen cruzado bloqueada: la política del mismo origen no permite leer el recurso remoto en
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Motivo: la solicitud CORS no se realizó correctamente). Más información - Chromium basado: la directiva de CORS ha bloqueado el acceso para capturar en "https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5" desde el origenhttps://cors3.azurewebsites.net: La respuesta a la solicitud preparatoria no pasa la comprobación de control de acceso: no hay ningún encabezado "Access-Control-Allow-Origin" presente en el recurso solicitado. Si una respuesta opaca sirve a sus necesidades, establezca el modo de la solicitud en 'no-cors' para obtener el recurso con CORS deshabilitado.
Para permitir encabezados específicos, llame a 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();
Para permitir todos los encabezados de solicitud de autor, llama a 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();
Los navegadores no son consistentes en la forma en que configuran Access-Control-Request-Headers
. Si alguna de las dos opciones:
- Los encabezados se establecen en cualquier otro valor que no sea
"*"
- AllowAnyHeader se llama a : incluya al menos
Accept
,Content-Type
yOrigin
, además de los encabezados personalizados que quiera admitir.
Código de solicitud de preparatoria automático
Cuando se aplica la directiva CORS:
- Globalmente llamando a
app.UseCors
enProgram.cs
. - Uso del atributo
[EnableCors]
.
ASP.NET Core responde a la solicitud preparatoria de OPTIONS.
La sección CORS de prueba de este documento muestra este comportamiento.
Atributo [HttpOptions] para solicitudes preparatorias
Cuando CORS está habilitado con la directiva adecuada, ASP.NET Core suele responder automáticamente a las solicitudes previas de CORS.
El código siguiente usa el atributo [HttpOptions] para crear puntos de conexión para las solicitudes 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);
}
Consulte Prueba CORS con el atributo [EnableCors] y el método RequireCors para obtener instrucciones sobre cómo probar el código anterior.
Establecer la hora de expiración previa
El encabezado Access-Control-Max-Age
especifica cuánto tiempo se puede almacenar en caché la respuesta a la solicitud preparatoria. Para establecer este encabezado, llame a 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();
Habilitación de CORS en un punto de conexión
Cómo funciona la CORS
Esta sección describe lo que sucede en una solicitud CORS a nivel de los mensajes HTTP.
- CORS no es una función de seguridad. El uso compartido de recursos entre orígenes (CORS) es una norma de W3C que permite mayor flexibilidad a los servidores en la directiva de mismo origen.
- Por ejemplo, un actor malicioso podría usar Cross-Site Scripting (XSS) contra su sitio y ejecutar una solicitud entre sitios a su sitio habilitado para CORS para robar información.
- Una API no es más segura al permitir CORS.
- Es necesario que el cliente (explorador) aplique CORS. El servidor ejecuta la solicitud y devuelve la respuesta, es el cliente que devuelve un error y bloquea la respuesta. Por ejemplo, cualquiera de las siguientes herramientas mostrará la respuesta del servidor:
- Fiddler
- .NET HttpClient
- Un explorador web escribiendo la dirección URL en la barra de direcciones.
- Es necesario que el cliente (explorador) aplique CORS. El servidor ejecuta la solicitud y devuelve la respuesta, es el cliente que devuelve un error y bloquea la respuesta. Por ejemplo, cualquiera de las siguientes herramientas mostrará la respuesta del servidor:
- Es una forma de que un servidor permita a los navegadores ejecutar una solicitud XHR o Fetch API de origen cruzado que, de lo contrario, estaría prohibida.
- Los navegadores sin CORS no pueden hacer solicitudes de origen cruzado. Antes de CORS, se usaba JSONP para eludir esta restricción. JSONP no utiliza XHR, utiliza la etiqueta
<script>
para recibir la respuesta. Se permite cargar scripts entre orígenes.
- Los navegadores sin CORS no pueden hacer solicitudes de origen cruzado. Antes de CORS, se usaba JSONP para eludir esta restricción. JSONP no utiliza XHR, utiliza la etiqueta
La especificación CORS introdujo varios encabezados HTTP nuevos que permiten solicitudes de origen cruzado. Si un explorador admite CORS, establece estos encabezados automáticamente para las solicitudes entre orígenes. El código JavaScript personalizado no es necesario para habilitar CORS.
El siguiente es un ejemplo de una solicitud de origen cruzado desde el botón de prueba Valores a https://cors1.azurewebsites.net/api/values
. El encabezado Origin
:
- Proporciona el dominio del sitio que realiza la solicitud.
- Es necesario y debe ser diferente del host.
Encabezados generales
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
Encabezados de respuesta
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
Encabezados de solicitud
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 ...
En las solicitudes OPTIONS
, el servidor establece el encabezado Encabezados de respuesta Access-Control-Allow-Origin: {allowed origin}
en la respuesta. Por ejemplo, en el código de ejemplo, la solicitud OPTIONS
del botón Delete [EnableCors]
contiene los siguientes encabezados:
Encabezados generales
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
Encabezados de respuesta
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
Encabezados de solicitud
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
En los encabezados de respuesta anteriores, el servidor establece el encabezado Access-Control-Allow-Origin en la respuesta. El valor https://cors1.azurewebsites.net
de este encabezado coincide con el encabezado Origin
de la solicitud.
Si se llama aAllowAnyOrigin, el Access-Control-Allow-Origin: *
se devuelve el valor comodín . AllowAnyOrigin
permite cualquier origen.
Si la respuesta no incluye el encabezado Access-Control-Allow-Origin
, la solicitud de origen cruzado falla. En concreto, el navegador no permite la solicitud. Incluso si el servidor devuelve una respuesta correcta, el explorador no hace que la respuesta esté disponible para la aplicación cliente.
El redireccionamiento HTTP a HTTPS provoca ERR_INVALID_REDIRECT en la solicitud de preparatoria de CORS
Las solicitudes a un punto de conexión usando HTTP que son redirigidas a HTTPS por UseHttpsRedirection fallan con ERR_INVALID_REDIRECT on the CORS preflight request
.
Los proyectos de API pueden rechazar solicitudes HTTP en lugar de usar UseHttpsRedirection
para redirigir solicitudes a HTTPS.
CORS en IIS
Al implementar en IIS, CORS debe ejecutarse antes de la autenticación de Windows si el servidor no está configurado para permitir el acceso anónimo. Para admitir este escenario, el módulo CORS de IIS debe instalarse y configurarse para la aplicación.
Prueba de CORS
La descarga de muestra tiene código para probar CORS. Vea cómo descargarlo. El ejemplo es un proyecto de API con Razor páginas agregadas:
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();
Advertencia
WithOrigins("https://localhost:<port>");
solo se debería usar para probar una aplicación de ejemplo similar al código de ejemplo de descarga.
A continuación ValuesController
se proporcionan los puntos de conexión para las pruebas:
[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();
}
El paquete de NuGet Rick.Docs.Samples.RouteInfo proporciona MyDisplayRouteInfo y se muestra la información de ruta.
Pruebe el código de ejemplo anterior mediante uno de los métodos siguientes:
- Ejecute el ejemplo con
dotnet run
utilizando la URL predeterminada dehttps://localhost:5001
. - Ejecute el ejemplo desde Visual Studio con el puerto establecido en 44398 para una URL de
https://localhost:44398
.
Uso de un explorador con las herramientas F12:
Seleccione el botón Valores y revise los encabezados en la pestaña Red.
Seleccione el botón probar PUT. Consulte Mostrar solicitudes de OPCIONES para obtener instrucciones sobre cómo mostrar la solicitud de OPCIONES. La prueba PUT crea dos solicitudes, una solicitud preparatoria de OPCIONES y la solicitud PUT.
Seleccione el
GetValues2 [DisableCors]
botón para desencadenar una solicitud CORS con error. Como se mencionó en el documento, la respuesta devuelve 200 correcto, pero no se realiza la solicitud CORS. Seleccione la pestaña Consola para ver el error de CORS. Dependiendo del navegador, se muestra un error similar al siguiente:El acceso a la obtención en
'https://cors1.azurewebsites.net/api/values/GetValues2'
desde el origen'https://cors3.azurewebsites.net'
ha sido bloqueado por la política de CORS: no hay encabezado 'Access-Control-Allow-Origin' presente en el recurso solicitado. Si una respuesta opaca sirve a sus necesidades, establezca el modo de la solicitud en 'no-cors' para obtener el recurso con CORS deshabilitado.
Los puntos finales habilitados para CORS se pueden probar con una herramienta, como curl o Fiddler. Cuando se utiliza una herramienta, el origen de la solicitud especificada por el encabezado Origin
debe diferir del host que recibe la solicitud. Si la solicitud no es de origen cruzado según el valor del encabezado Origin
:
- No es necesario que el middleware de CORS procese la solicitud.
- Los encabezados CORS no se devuelven en la respuesta.
El siguiente comando usa curl
para emitir una solicitud OPTIONS con información:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
Prueba de CORS con el atributo [EnableCors] y el método RequireCors
Considere el siguiente código que utiliza enrutamiento de punto final para habilitar CORS por punto final utilizando RequireCors
:
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();
Observe que solo el /echo
punto de conexión está utilizando el RequireCors
para permitir solicitudes de origen cruzado utilizando la directiva especificada. Los controladores siguientes habilitan CORS mediante el atributo [EnableCors].
A continuación TodoItems1Controller
se proporcionan los puntos de conexión para las pruebas:
[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);
}
Los botones Delete [EnableCors] y GET [EnableCors] tienen éxito, porque los puntos finales tienen [EnableCors]
y responden a las solicitudes preparatorias. Se produce un error en los demás puntos de conexión. El botón GET falla, porque el JavaScript envía:
headers: {
"Content-Type": "x-custom-header"
},
A continuación TodoItems2Controller
se proporcionan puntos de conexión similares, pero se incluye código explícito para responder a las solicitudes 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);
}
El código anterior se puede probar mediante la implementación del ejemplo en Azure. En la lista desplegable Controlador, seleccione Preflight y, a continuación, Establecer controlador. Todas las llamadas CORS a los puntos finales TodoItems2Controller
tienen éxito.
Recursos adicionales
Por Rick Anderson y Kirk Larkin
En este artículo se muestra cómo habilitar CORS en una aplicación de ASP.NET Core.
La seguridad del explorador evita que una página web realice solicitudes a un dominio diferente del que atendió a dicha página web. Esta restricción se denomina directiva de mismo origen. La directiva de mismo origen evita que un sitio malintencionado lea información confidencial de otro sitio. A veces, es posible que quiera permitir que otros sitios realicen solicitudes entre orígenes a la aplicación. Para obtener más información, consulte el artículo CORS de Mozilla.
Intercambio de recursos de origen cruzado (CORS):
- Es una norma de W3C que permite mayor flexibilidad a los servidores en la directiva de mismo origen.
- No es una característica de seguridad, CORS relaja la seguridad. Una API no es más segura al permitir CORS. Para más información, vea Funcionamiento de CORS.
- Permite explícitamente a un servidor a permitir algunas solicitudes de origen cruzado y rechazar otras.
- Es más seguro y flexible que las técnicas anteriores, como JSONP.
Vea o descargue el código de ejemplo (cómo descargarlo)
Mismo origen
Dos URL tienen el mismo origen si tienen esquemas, hosts y puertos idénticos (RFC 6454).
Estas dos direcciones URL tienen el mismo origen:
https://example.com/foo.html
https://example.com/bar.html
Estas direcciones URL tienen orígenes diferentes a las dos direcciones URL anteriores:
https://example.net
: Dominio diferentehttps://www.example.com/foo.html
: Subdominio diferentehttp://example.com/foo.html
: Esquema diferentehttps://example.com:9000/foo.html
: Puerto diferente
Habilitación de CORS
Existen tres formas de habilitar CORS:
- En middleware mediante una directiva con nombre o directiva predeterminada .
- Utilizando el enrutamiento de punto de conexión.
- Con el atributo [EnableCors].
El uso del atributo [EnableCors] con una directiva con nombre proporciona el mejor control para limitar los puntos finales que admiten CORS.
Advertencia
UseCors debe llamarse en el orden correcto. Para obtener más información vea Orden de Middleware. Por ejemplo, UseCors
debe llamarse antes de UseResponseCaching cuando se utiliza UseResponseCaching
.
Cada ennfoque se detalla en las secciones siguientes.
CORS con directivas con nombre y middleware
El middleware de CORS controla las solicitudes entre orígenes. El código siguiente aplica una directiva de CORS a todos los puntos de conexión de la aplicación con los orígenes especificados:
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();
El código anterior:
- Establece el nombre de la directiva en
_myAllowSpecificOrigins
. El nombre de la política es arbitrario. - Llama al UseCorsmétodo de extensión y especifica la
_myAllowSpecificOrigins
directiva CORS.UseCors
agrega el middleware CORS. La llamada aUseCors
debe colocarse después deUseRouting
, pero antes deUseAuthorization
. Para obtener más información vea Orden de Middleware. - Llama a AddCors con una expresión lambda . La lambda toma un objeto CorsPolicyBuilder. Las opciones de configuración, como
WithOrigins
, se describen más adelante en este artículo. - Habilita la
_myAllowSpecificOrigins
directiva de CORS para todos los puntos de conexión del controlador. Consulte enrutamiento de punto final para aplicar una directiva CORS a puntos finales específicos. - Al usar middleware de almacenamiento en caché de respuesta, llame UseCors antes de UseResponseCaching.
Con el enrutamiento de puntos de conexión, el middleware de CORS debe configurarse para ejecutarse entre las llamadas a UseRouting
y UseEndpoints
.
La AddCors llamada al método añade servicios CORS al contenedor de servicios de la aplicación:
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();
Para obtener más información, consulte Opciones de directiva CORS en este documento.
Los CorsPolicyBuilder métodos se pueden encadenar, como se muestra en el código siguiente:
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();
Nota: La URL especificada no debe contener una barra diagonal (/
). Si la dirección URL finaliza con /
, la comparación devuelve false
y no se devuelve ningún encabezado.
Advertencia
UseCors
debe colocarse después UseRouting
y antes de UseAuthorization
. Esto es para asegurarse de que los encabezados CORS se incluyen en la respuesta para las llamadas autorizadas y no autorizadas.
Orden de UseCors y UseStaticFiles
Normalmente, UseStaticFiles
se llama a antes de UseCors
. Las aplicaciones que usan JavaScript para recuperar archivos estáticos entre sitios deben llamar a UseCors
antes de UseStaticFiles
.
CORS con directivas predeterminadas y middleware
El código resaltado siguiente habilita la directiva CORS predeterminada:
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();
El código anterior aplica la directiva CORS predeterminada a todos los puntos de conexión del controlador.
Habilitación de CORS con enrutamiento de punto de conexión
Con el enrutamiento de punto final, CORS se puede habilitar por punto final utilizando el RequireCors conjunto de métodos de extensión:
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();
En el código anterior:
app.UseCors
agrega el middleware CORS. Debido a que no se ha configurado una política predeterminada,app.UseCors()
por sí solo no habilita CORS.- Los
/echo
y puntos de conexión del controlador permiten solicitudes entre orígenes mediante la directiva especificada. - Los
/echo2
y Razor puntos de conexión de páginas no permiten solicitudes de origen cruzado porque no se especificó ninguna política predeterminada.
El [DisableCors]atributo no deshabilita el CORS que ha sido habilitado por el enrutamiento de punto final con RequireCors
.
En ASP.NET Core 7.0, el [EnableCors]
atributo debe pasar un parámetro o una advertencia ASP0023 a partir de una coincidencia ambigua en la ruta. ASP.NET Core 8.0 y versiones posteriores no genera la ASP0023
advertencia.
[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);
}
Consulte Probar CORS con el atributo [EnableCors] y el método RequireCors para obtener instrucciones sobre cómo probar código similar al anterior.
Habilitación de CORS con atributos
Habilitar CORS con el atributo [EnableCors] y aplicar una directiva con nombre solo a los puntos de conexión que requieren CORS proporcionan el mejor control.
El atributo [EnableCors] proporciona una alternativa a aplicar CORS globalmente. El [EnableCors]
atributo habilita CORS para los puntos de conexión seleccionados, en lugar de todos los puntos de conexión:
[EnableCors]
especifica la política predeterminada.[EnableCors("{Policy String}")]
especifica una directiva guardada.
El atributo [EnableCors]
se puede aplicar a:
- Razor Página
PageModel
- Controlador
- Método de acción del controlador
Se pueden aplicar diferentes directivas a controladores, modelos de página o métodos de acción con el atributo [EnableCors]
. Cuando el atributo [EnableCors]
se aplica a un controlador, modelo de página o método de acción, y CORS está habilitado en middleware, se aplican ambas directivas. Se recomienda combinar directivas. Use el el atributo o middleware [EnableCors]
, no ambos, en la misma aplicación.
El código siguiente aplica una directiva diferente a cada método:
[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(),
};
}
}
El siguiente código crea dos políticas 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();
Para obtener el mejor control de la limitación de solicitudes CORS:
- Use
[EnableCors("MyPolicy")]
con una directiva con nombre. - No defina una directiva predeterminada.
- No utilice enrutamiento de punto final .
El código de la sección siguiente cumple la lista anterior.
Deshabilitación de CORS
El atributo [DisableCors]no deshabilita el CORS que ha sido habilitado por el enrutamiento de punto final .
El siguiente código define la política 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();
El código siguiente deshabilita CORS para la GetValues2
acción:
[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();
}
El código anterior:
- No habilita CORS con enrutamiento de punto final .
- No define una directiva CORS predeterminada.
- Usa [EnableCors("MyPolicy")] para habilitar la
"MyPolicy"
directiva CORS para el controlador. - Deshabilita CORS para el
GetValues2
método .
Consulte Probar CORS para obtener instrucciones sobre cómo probar el código anterior.
Opciones de directiva CORS
En esta sección se describen las distintas opciones que se pueden establecer en una directiva de CORS:
- Establecimiento de los orígenes permitidos
- Establecer los métodos HTTP permitidos
- Establecer los encabezados de solicitud permitidos
- Establecer los encabezados de respuesta expuestos
- Credenciales en solicitudes entre orígenes
- Establecer la hora de expiración previa
AddPolicy es llamado por Program.cs
. Para algunas opciones, puede resultar útil leer primero la sección Funcionamiento de CORS .
Establecimiento de los orígenes permitidos
AllowAnyOrigin: Permite solicitudes CORS desde todos los orígenes con cualquier esquema (http
o https
). AllowAnyOrigin
no es seguro porque cualquier sitio web puede realizar solicitudes entre orígenes a la aplicación.
Nota
Especificar AllowAnyOrigin
y AllowCredentials
es una configuración no segura y puede dar lugar a una falsificación de solicitud entre sitios. El servicio CORS devuelve una respuesta CORS no válida cuando una aplicación está configurada con ambos métodos.
AllowAnyOrigin
afecta a las solicitudes preparatorias y al Access-Control-Allow-Origin
encabezado. Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
SetIsOriginAllowedToAllowWildcardSubdomains: establece la IsOriginAllowed propiedad de la directiva en una función que permite que los orígenes coincidan con un dominio comodín configurado al evaluar si se permite el origen.
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();
Establecimiento de los métodos HTTP permitidos
- Permite cualquier método HTTP:
- Afecta a las solicitudes preparatorias y al encabezado
Access-Control-Allow-Methods
. Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
Establecimiento de los encabezados de solicitud permitidos
Para permitir que se envíen encabezados específicos en una solicitud CORS, llamada encabezados de solicitud de autor , llame a WithHeaders y especifique los encabezados permitidos:
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();
Para permitir todos los encabezados de solicitud de autor, llame a 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
afecta a las solicitudes de preventa y al encabezado Access-Control-Request-Headers . Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
Una coincidencia de política de CORS Middleware con encabezados específicos especificados por WithHeaders
solo es posible cuando los encabezados enviados en Access-Control-Request-Headers
coinciden exactamente con los encabezados indicados en WithHeaders
.
Por ejemplo, considere la posibilidad de configurar una aplicación como se indica a continuación:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
CORS Middleware rechaza la solicitud preparatoria con el siguiente encabezado de solicitud porque Content-Language
(HeaderNames.ContentLanguage ) no aparece en WithHeaders
:
Access-Control-Request-Headers: Cache-Control, Content-Language
La aplicación devuelve una respuesta 200 OK pero no devuelve los encabezados CORS. Por lo tanto, el explorador no intenta la solicitud entre orígenes.
Establecer los encabezados de respuesta expuestos
De forma predeterminada, el explorador no expone todos los encabezados de respuesta a la aplicación. Para obtener más información, consulte W3C Cross-Origin Resource Sharing (Terminology): Simple Response Header.
Los encabezados de respuesta que están disponibles de forma predeterminada son:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
La especificación CORS llama a estos encabezados de respuesta simples. Para que otros encabezados estén disponibles para la aplicación, llame a 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();
Credenciales en solicitudes entre orígenes
Las credenciales requieren un control especial en una solicitud CORS. De forma predeterminada, el navegador no envía credenciales con una solicitud de origen cruzado. Las credenciales incluyen cookies y esquemas de autenticación HTTP. Para enviar credenciales con una solicitud de origen cruzado, el cliente debe establecer XMLHttpRequest.withCredentials
en true
.
Uso XMLHttpRequest
directo:
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
Uso de jQuery:
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
Uso de la Fetch API:
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
El servidor debe permitir las credenciales. Para permitir credenciales de origen cruzado, llame a 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 respuesta HTTP incluye un encabezado Access-Control-Allow-Credentials
, que le dice al navegador que el servidor permite credenciales para una solicitud de origen cruzado.
Si el explorador envía credenciales, pero la respuesta no incluye un encabezado válido Access-Control-Allow-Credentials
, el explorador no expone la respuesta a la aplicación y se produce un error en la solicitud entre orígenes.
Permitir credenciales entre orígenes es un riesgo de seguridad. Un sitio web de otro dominio puede enviar las credenciales de un usuario que ha iniciado sesión a la aplicación en nombre del usuario sin el conocimiento del usuario.
La especificación CORS también indica que establecer orígenes en "*"
(todos los orígenes) no es válido si el Access-Control-Allow-Credentials
encabezado está presente.
Solicitud preparatoria
Para algunas solicitudes CORS, el navegador envía una solicitud adicional de OPTIONS antes de realizar la solicitud real. Esta solicitud se denomina solicitud preparatoria. El navegador puede omitir la solicitud preparatoria si se cumplen todas las siguientes condiciones:
- El método de solicitud es GET, HE0AD o POST.
- La aplicación no establece encabezados de solicitud que no sean
Accept
,Accept-Language
,Content-Language
,Content-Type
oLast-Event-ID
. - El encabezado
Content-Type
, si está configurado, tiene uno de los siguientes valores:application/x-www-form-urlencoded
multipart/form-data
text/plain
La regla sobre encabezados de solicitud establecida para la solicitud del cliente se aplica a los encabezados que la aplicación establece llamando a setRequestHeader
en el objeto XMLHttpRequest
. La especificación CORS llama a estos encabezados de solicitud de autor. La regla no se aplica a los encabezados que el explorador puede establecer, como User-Agent
, Host
o Content-Length
.
La siguiente es una respuesta de ejemplo similar a la solicitud preparatoria realizada desde el botón [Put test] en la sección Test CORS de este documento.
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 solicitud preparatoria utiliza el método OPCIONES HTTP . Puede incluir los siguientes encabezados:
- Access-Control-Request-Method: método HTTP que se usará para la solicitud real.
- Access-Control-Request-Headers: una lista de encabezados de solicitud que la aplicación establece en la solicitud real. Como se indicó anteriormente, esto no incluye encabezados que establece el explorador, como
User-Agent
. - Métodos Access-Control-Allow
Si se rechaza la solicitud de preventa, la aplicación devuelve una respuesta 200 OK
pero no establece los encabezados CORS. Por lo tanto, el explorador no intenta la solicitud entre orígenes. Para obtener un ejemplo de una solicitud preparatoria denegada, consulte la sección Probar CORS de este documento.
Con las herramientas F12, la aplicación de consola muestra un error similar a uno de los siguientes, según el explorador:
- Firefox: solicitud de origen cruzado bloqueada: la política del mismo origen no permite leer el recurso remoto en
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Motivo: la solicitud CORS no se realizó correctamente). Más información - Chromium basado: la directiva de CORS ha bloqueado el acceso para capturar en "https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5" desde el origenhttps://cors3.azurewebsites.net: La respuesta a la solicitud preparatoria no pasa la comprobación de control de acceso: no hay ningún encabezado "Access-Control-Allow-Origin" presente en el recurso solicitado. Si una respuesta opaca sirve a sus necesidades, establezca el modo de la solicitud en 'no-cors' para obtener el recurso con CORS deshabilitado.
Para permitir encabezados específicos, llame a 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();
Para permitir todos los encabezados de solicitud de autor, llame a 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();
Los navegadores no son consistentes en la forma en que configuran Access-Control-Request-Headers
. Si alguna de las dos opciones:
- Los encabezados se establecen en cualquier otro valor que no sea
"*"
- AllowAnyHeader se llama a : incluya al menos
Accept
,Content-Type
yOrigin
, además de los encabezados personalizados que quiera admitir.
Código de solicitud de preparatoria automático
Cuando se aplica la directiva CORS:
- Globalmente llamando a
app.UseCors
enProgram.cs
. - Uso del atributo
[EnableCors]
.
ASP.NET Core responde a la solicitud preparatoria de OPTIONS.
La sección CORS de prueba de este documento muestra este comportamiento.
Atributo [HttpOptions] para solicitudes preparatorias
Cuando CORS está habilitado con la directiva adecuada, ASP.NET Core suele responder automáticamente a las solicitudes previas de CORS.
El código siguiente usa el atributo [HttpOptions] para crear puntos de conexión para las solicitudes 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);
}
Consulte Prueba CORS con el atributo [EnableCors] y el método RequireCors para obtener instrucciones sobre cómo probar el código anterior.
Establecer la hora de expiración previa
El encabezado Access-Control-Max-Age
especifica cuánto tiempo se puede almacenar en caché la respuesta a la solicitud preparatoria. Para establecer este encabezado, llame a 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();
Habilitación de CORS en un punto de conexión
Cómo funciona la CORS
Esta sección describe lo que sucede en una solicitud CORS a nivel de los mensajes HTTP.
- CORS no es una función de seguridad. El uso compartido de recursos entre orígenes (CORS) es una norma de W3C que permite mayor flexibilidad a los servidores en la directiva de mismo origen.
- Por ejemplo, un actor malicioso podría usar Cross-Site Scripting (XSS) contra su sitio y ejecutar una solicitud entre sitios a su sitio habilitado para CORS para robar información.
- Una API no es más segura al permitir CORS.
- Es necesario que el cliente (explorador) aplique CORS. El servidor ejecuta la solicitud y devuelve la respuesta, es el cliente que devuelve un error y bloquea la respuesta. Por ejemplo, cualquiera de las siguientes herramientas mostrará la respuesta del servidor:
- Fiddler
- .NET HttpClient
- Un explorador web escribiendo la dirección URL en la barra de direcciones.
- Es necesario que el cliente (explorador) aplique CORS. El servidor ejecuta la solicitud y devuelve la respuesta, es el cliente que devuelve un error y bloquea la respuesta. Por ejemplo, cualquiera de las siguientes herramientas mostrará la respuesta del servidor:
- Es una forma de que un servidor permita a los navegadores ejecutar una solicitud XHR o Fetch API de origen cruzado que, de lo contrario, estaría prohibida.
- Los navegadores sin CORS no pueden hacer solicitudes de origen cruzado. Antes de CORS, se usaba JSONP para eludir esta restricción. JSONP no utiliza XHR, utiliza la etiqueta
<script>
para recibir la respuesta. Se permite cargar scripts entre orígenes.
- Los navegadores sin CORS no pueden hacer solicitudes de origen cruzado. Antes de CORS, se usaba JSONP para eludir esta restricción. JSONP no utiliza XHR, utiliza la etiqueta
La especificación CORS introdujo varios encabezados HTTP nuevos que permiten solicitudes de origen cruzado. Si un explorador admite CORS, establece estos encabezados automáticamente para las solicitudes entre orígenes. El código JavaScript personalizado no es necesario para habilitar CORS.
Selecciona el botón de prueba PUT de la muestra implementada.
El encabezado Origin
:
- Proporciona el dominio del sitio que realiza la solicitud.
- Es necesario y debe ser diferente del host.
Encabezados generales
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
Encabezados de respuesta
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
Encabezados de solicitud
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 ...
En las solicitudes OPTIONS
, el servidor establece el encabezado Encabezados de respuesta Access-Control-Allow-Origin: {allowed origin}
en la respuesta. Por ejemplo, en el código de ejemplo, la solicitud OPTIONS
del botón Delete [EnableCors]
contiene los siguientes encabezados:
Encabezados generales
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
Encabezados de respuesta
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
Encabezados de solicitud
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
En los encabezados de respuesta anteriores, el servidor establece el encabezado Access-Control-Allow-Origin en la respuesta. El valor https://cors1.azurewebsites.net
de este encabezado coincide con el encabezado Origin
de la solicitud.
Si se llama aAllowAnyOrigin, el Access-Control-Allow-Origin: *
se devuelve el valor comodín . AllowAnyOrigin
permite cualquier origen.
Si la respuesta no incluye el encabezado Access-Control-Allow-Origin
, la solicitud de origen cruzado falla. En concreto, el navegador no permite la solicitud. Incluso si el servidor devuelve una respuesta correcta, el explorador no hace que la respuesta esté disponible para la aplicación cliente.
El redireccionamiento HTTP a HTTPS provoca ERR_INVALID_REDIRECT en la solicitud de preparatoria de CORS
Las solicitudes a un punto de conexión usando HTTP que son redirigidas a HTTPS por UseHttpsRedirection fallan con ERR_INVALID_REDIRECT on the CORS preflight request
.
Los proyectos de API pueden rechazar solicitudes HTTP en lugar de usar UseHttpsRedirection
para redirigir solicitudes a HTTPS.
CORS en IIS
Al implementar en IIS, CORS debe ejecutarse antes de la autenticación de Windows si el servidor no está configurado para permitir el acceso anónimo. Para admitir este escenario, el módulo CORS de IIS debe instalarse y configurarse para la aplicación.
Prueba de CORS
La descarga de muestra tiene código para probar CORS. Vea cómo descargarlo. El ejemplo es un proyecto de API con Razor páginas agregadas:
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();
Advertencia
WithOrigins("https://localhost:<port>");
solo se debería usar para probar una aplicación de ejemplo similar al código de ejemplo de descarga.
A continuación ValuesController
se proporcionan los puntos de conexión para las pruebas:
[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();
}
El paquete de NuGet Rick.Docs.Samples.RouteInfo proporciona MyDisplayRouteInfo y se muestra la información de ruta.
Pruebe el código de ejemplo anterior mediante uno de los métodos siguientes:
- Ejecute el ejemplo con
dotnet run
utilizando la URL predeterminada dehttps://localhost:5001
. - Ejecute el ejemplo desde Visual Studio con el puerto establecido en 44398 para una URL de
https://localhost:44398
.
Uso de un explorador con las herramientas F12:
Seleccione el botón Valores y revise los encabezados en la pestaña Red.
Seleccione el botón probar PUT. Consulte Mostrar solicitudes de OPCIONES para obtener instrucciones sobre cómo mostrar la solicitud de OPCIONES. La prueba PUT crea dos solicitudes, una solicitud preparatoria de OPCIONES y la solicitud PUT.
Seleccione el
GetValues2 [DisableCors]
botón para desencadenar una solicitud CORS con error. Como se mencionó en el documento, la respuesta devuelve 200 correcto, pero no se realiza la solicitud CORS. Seleccione la pestaña Consola para ver el error de CORS. Dependiendo del navegador, se muestra un error similar al siguiente:El acceso a la obtención en
'https://cors1.azurewebsites.net/api/values/GetValues2'
desde el origen'https://cors3.azurewebsites.net'
ha sido bloqueado por la política de CORS: no hay encabezado 'Access-Control-Allow-Origin' presente en el recurso solicitado. Si una respuesta opaca sirve a sus necesidades, establezca el modo de la solicitud en 'no-cors' para obtener el recurso con CORS deshabilitado.
Los puntos finales habilitados para CORS se pueden probar con una herramienta, como curl o Fiddler. Cuando se utiliza una herramienta, el origen de la solicitud especificada por el encabezado Origin
debe diferir del host que recibe la solicitud. Si la solicitud no es de origen cruzado según el valor del encabezado Origin
:
- No es necesario que el middleware de CORS procese la solicitud.
- Los encabezados CORS no se devuelven en la respuesta.
El siguiente comando usa curl
para emitir una solicitud OPTIONS con información:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
Prueba de CORS con el atributo [EnableCors] y el método RequireCors
Considere el siguiente código que utiliza enrutamiento de punto final para habilitar CORS por punto final utilizando RequireCors
:
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();
Observe que solo el /echo
punto de conexión está utilizando el RequireCors
para permitir solicitudes de origen cruzado utilizando la directiva especificada. Los controladores siguientes habilitan CORS mediante el atributo [EnableCors].
A continuación TodoItems1Controller
se proporcionan los puntos de conexión para las pruebas:
[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);
}
Los botones Delete [EnableCors] y GET [EnableCors] tienen éxito, porque los puntos finales tienen [EnableCors]
y responden a las solicitudes preparatorias. Se produce un error en los demás puntos de conexión. El botón GET falla, porque el JavaScript envía:
headers: {
"Content-Type": "x-custom-header"
},
A continuación TodoItems2Controller
se proporcionan puntos de conexión similares, pero se incluye código explícito para responder a las solicitudes 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);
}
El código anterior se puede probar al implementar la muestra en Azure. En la lista desplegable Controlador, selecciona Preparatorio y después Configurar controlador. Todas las llamadas CORS a los puntos finales TodoItems2Controller
tienen éxito.
Recursos adicionales
Por Rick Anderson y Kirk Larkin
En este artículo se muestra cómo habilitar CORS en una aplicación de ASP.NET Core.
La seguridad del explorador evita que una página web realice solicitudes a un dominio diferente del que atendió a dicha página web. Esta restricción se denomina directiva de mismo origen. La directiva de mismo origen evita que un sitio malintencionado lea información confidencial de otro sitio. A veces, es posible que quiera permitir que otros sitios realicen solicitudes entre orígenes a la aplicación. Para obtener más información, consulte el artículo CORS de Mozilla.
Intercambio de recursos de origen cruzado (CORS):
- Es una norma de W3C que permite mayor flexibilidad a los servidores en la directiva de mismo origen.
- No es una característica de seguridad, CORS relaja la seguridad. Una API no es más segura al permitir CORS. Para más información, vea Funcionamiento de CORS.
- Permite explícitamente a un servidor a permitir algunas solicitudes de origen cruzado y rechazar otras.
- Es más seguro y flexible que las técnicas anteriores, como JSONP.
Vea o descargue el código de ejemplo (cómo descargarlo)
Mismo origen
Dos URL tienen el mismo origen si tienen esquemas, hosts y puertos idénticos (RFC 6454).
Estas dos direcciones URL tienen el mismo origen:
https://example.com/foo.html
https://example.com/bar.html
Estas direcciones URL tienen orígenes diferentes a las dos direcciones URL anteriores:
https://example.net
: Dominio diferentehttps://www.example.com/foo.html
: Subdominio diferentehttp://example.com/foo.html
: Esquema diferentehttps://example.com:9000/foo.html
: Puerto diferente
Habilitación de CORS
Existen tres formas de habilitar CORS:
- En middleware mediante una directiva con nombre o directiva predeterminada .
- Utilizando el enrutamiento de punto de conexión.
- Con el atributo [EnableCors].
El uso del atributo [EnableCors] con una directiva con nombre proporciona el mejor control para limitar los puntos finales que admiten CORS.
Advertencia
UseCors debe llamarse en el orden correcto. Para obtener más información vea Orden de Middleware. Por ejemplo, UseCors
debe llamarse antes de UseResponseCaching cuando se utiliza UseResponseCaching
.
Cada ennfoque se detalla en las secciones siguientes.
CORS con directivas con nombre y middleware
El middleware de CORS controla las solicitudes entre orígenes. El código siguiente aplica una directiva de CORS a todos los puntos de conexión de la aplicación con los orígenes especificados:
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();
El código anterior:
- Establece el nombre de la directiva en
_myAllowSpecificOrigins
. El nombre de la política es arbitrario. - Llama al UseCorsmétodo de extensión y especifica la
_myAllowSpecificOrigins
directiva CORS.UseCors
agrega el middleware CORS. La llamada aUseCors
debe colocarse después deUseRouting
, pero antes deUseAuthorization
. Para obtener más información vea Orden de Middleware. - Llama a AddCors con una expresión lambda . La lambda toma un objeto CorsPolicyBuilder. Las opciones de configuración, como
WithOrigins
, se describen más adelante en este artículo. - Habilita la
_myAllowSpecificOrigins
directiva de CORS para todos los puntos de conexión del controlador. Consulte enrutamiento de punto final para aplicar una directiva CORS a puntos finales específicos. - Al usar middleware de almacenamiento en caché de respuesta, llame UseCors antes de UseResponseCaching.
Con el enrutamiento de puntos de conexión, el middleware de CORS debe configurarse para ejecutarse entre las llamadas a UseRouting
y UseEndpoints
.
La AddCors llamada al método añade servicios CORS al contenedor de servicios de la aplicación:
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();
Para obtener más información, consulte Opciones de directiva CORS en este documento.
Los CorsPolicyBuilder métodos se pueden encadenar, como se muestra en el código siguiente:
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();
Nota: La URL especificada no debe contener una barra diagonal (/
). Si la dirección URL finaliza con /
, la comparación devuelve false
y no se devuelve ningún encabezado.
Advertencia
UseCors
debe colocarse después UseRouting
y antes de UseAuthorization
. Esto es para asegurarse de que los encabezados CORS se incluyen en la respuesta para las llamadas autorizadas y no autorizadas.
Orden de UseCors y UseStaticFiles
Normalmente, UseStaticFiles
se llama a antes de UseCors
. Las aplicaciones que usan JavaScript para recuperar archivos estáticos entre sitios deben llamar a UseCors
antes de UseStaticFiles
.
CORS con directivas predeterminadas y middleware
El código resaltado siguiente habilita la directiva CORS predeterminada:
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();
El código anterior aplica la directiva CORS predeterminada a todos los puntos de conexión del controlador.
Habilitación de CORS con enrutamiento de punto de conexión
La habilitación de CORS por punto de conexión usando RequireCors
no admite solicitudes preparatorias automáticas. Para obtener más información, consulte este problema de GitHub y Pruebe CORS con enrutamiento de puntos de conexión y [HttpOptions].
Con el enrutamiento de punto final, CORS se puede habilitar por punto final utilizando el RequireCors conjunto de métodos de extensión:
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();
En el código anterior:
app.UseCors
agrega el middleware CORS. Debido a que no se ha configurado una política predeterminada,app.UseCors()
por sí solo no habilita CORS.- Los
/echo
y puntos de conexión del controlador permiten solicitudes entre orígenes mediante la directiva especificada. - Los
/echo2
y Razor puntos de conexión de páginas no permiten solicitudes de origen cruzado porque no se especificó ninguna política predeterminada.
El atributo [DisableCors]no deshabilita el CORS que ha sido habilitado por el enrutamiento de punto final con RequireCors
.
Consulte Prueba CORS con enrutamiento de punto final y [HttpOptions] para obtener instrucciones sobre el código de prueba similar al anterior.
Habilitación de CORS con atributos
Habilitar CORS con el atributo [EnableCors] y aplicar una directiva con nombre solo a los puntos de conexión que requieren CORS proporcionan el mejor control.
El atributo [EnableCors] proporciona una alternativa a aplicar CORS globalmente. El [EnableCors]
atributo habilita CORS para los puntos de conexión seleccionados, en lugar de todos los puntos de conexión:
[EnableCors]
especifica la política predeterminada.[EnableCors("{Policy String}")]
especifica una directiva guardada.
El atributo [EnableCors]
se puede aplicar a:
- Razor Página
PageModel
- Controlador
- Método de acción del controlador
Se pueden aplicar diferentes directivas a controladores, modelos de página o métodos de acción con el atributo [EnableCors]
. Cuando el atributo [EnableCors]
se aplica a un controlador, modelo de página o método de acción, y CORS está habilitado en middleware, se aplican ambas directivas. Se recomienda combinar directivas. Use el el atributo o middleware [EnableCors]
, no ambos, en la misma aplicación.
El código siguiente aplica una directiva diferente a cada método:
[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(),
};
}
}
El siguiente código crea dos políticas 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();
Para obtener el mejor control de la limitación de solicitudes CORS:
- Use
[EnableCors("MyPolicy")]
con una directiva con nombre. - No defina una directiva predeterminada.
- No utilice enrutamiento de punto final .
El código de la sección siguiente cumple la lista anterior.
Deshabilitación de CORS
El atributo [DisableCors]no deshabilita el CORS que ha sido habilitado por el enrutamiento de punto final .
El siguiente código define la política 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.MapControllers();
app.MapRazorPages();
app.Run();
El código siguiente deshabilita CORS para la GetValues2
acción:
[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();
}
El código anterior:
- No habilita CORS con enrutamiento de punto final .
- No define una directiva CORS predeterminada.
- Usa [EnableCors("MyPolicy")] para habilitar la
"MyPolicy"
directiva CORS para el controlador. - Deshabilita CORS para el
GetValues2
método .
Consulte Probar CORS para obtener instrucciones sobre cómo probar el código anterior.
Opciones de directiva CORS
En esta sección se describen las distintas opciones que se pueden establecer en una directiva de CORS:
- Establecimiento de los orígenes permitidos
- Establecer los métodos HTTP permitidos
- Establecer los encabezados de solicitud permitidos
- Establecer los encabezados de respuesta expuestos
- Credenciales en solicitudes entre orígenes
- Establecer la hora de expiración previa
AddPolicy es llamado por Program.cs
. Para algunas opciones, puede resultar útil leer primero la sección Funcionamiento de CORS .
Establecimiento de los orígenes permitidos
AllowAnyOrigin: Permite solicitudes CORS desde todos los orígenes con cualquier esquema (http
o https
). AllowAnyOrigin
no es seguro porque cualquier sitio web puede realizar solicitudes entre orígenes a la aplicación.
Nota
Especificar AllowAnyOrigin
y AllowCredentials
es una configuración no segura y puede dar lugar a una falsificación de solicitud entre sitios. El servicio CORS devuelve una respuesta CORS no válida cuando una aplicación está configurada con ambos métodos.
AllowAnyOrigin
afecta a las solicitudes preparatorias y al Access-Control-Allow-Origin
encabezado. Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
SetIsOriginAllowedToAllowWildcardSubdomains: establece la IsOriginAllowed propiedad de la directiva en una función que permite que los orígenes coincidan con un dominio comodín configurado al evaluar si se permite el origen.
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();
Establecimiento de los métodos HTTP permitidos
- Permite cualquier método HTTP:
- Afecta a las solicitudes preparatorias y al encabezado
Access-Control-Allow-Methods
. Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
Establecimiento de los encabezados de solicitud permitidos
Para permitir que se envíen encabezados específicos en una solicitud CORS, llamada encabezados de solicitud de autor , llame a WithHeaders y especifique los encabezados permitidos:
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();
Para permitir todos los encabezados de solicitud de autor, llame a 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
afecta a las solicitudes de preventa y al encabezado Access-Control-Request-Headers . Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
Una coincidencia de política de CORS Middleware con encabezados específicos especificados por WithHeaders
solo es posible cuando los encabezados enviados en Access-Control-Request-Headers
coinciden exactamente con los encabezados indicados en WithHeaders
.
Por ejemplo, considere la posibilidad de configurar una aplicación como se indica a continuación:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
CORS Middleware rechaza la solicitud preparatoria con el siguiente encabezado de solicitud porque Content-Language
(HeaderNames.ContentLanguage ) no aparece en WithHeaders
:
Access-Control-Request-Headers: Cache-Control, Content-Language
La aplicación devuelve una respuesta 200 OK pero no devuelve los encabezados CORS. Por lo tanto, el explorador no intenta la solicitud entre orígenes.
Establecer los encabezados de respuesta expuestos
De forma predeterminada, el explorador no expone todos los encabezados de respuesta a la aplicación. Para obtener más información, consulte W3C Cross-Origin Resource Sharing (Terminology): Simple Response Header.
Los encabezados de respuesta que están disponibles de forma predeterminada son:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
La especificación CORS llama a estos encabezados de respuesta simples. Para que otros encabezados estén disponibles para la aplicación, llame a 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();
Credenciales en solicitudes entre orígenes
Las credenciales requieren un control especial en una solicitud CORS. De forma predeterminada, el navegador no envía credenciales con una solicitud de origen cruzado. Las credenciales incluyen cookies y esquemas de autenticación HTTP. Para enviar credenciales con una solicitud de origen cruzado, el cliente debe establecer XMLHttpRequest.withCredentials
en true
.
Uso XMLHttpRequest
directo:
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
Uso de jQuery:
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
Uso de la Fetch API:
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
El servidor debe permitir las credenciales. Para permitir credenciales de origen cruzado, llame a 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 respuesta HTTP incluye un encabezado Access-Control-Allow-Credentials
, que le dice al navegador que el servidor permite credenciales para una solicitud de origen cruzado.
Si el explorador envía credenciales, pero la respuesta no incluye un encabezado válido Access-Control-Allow-Credentials
, el explorador no expone la respuesta a la aplicación y se produce un error en la solicitud entre orígenes.
Permitir credenciales entre orígenes es un riesgo de seguridad. Un sitio web de otro dominio puede enviar las credenciales de un usuario que ha iniciado sesión a la aplicación en nombre del usuario sin el conocimiento del usuario.
La especificación CORS también indica que establecer orígenes en "*"
(todos los orígenes) no es válido si el Access-Control-Allow-Credentials
encabezado está presente.
Solicitud preparatoria
Para algunas solicitudes CORS, el navegador envía una solicitud adicional de OPTIONS antes de realizar la solicitud real. Esta solicitud se denomina solicitud preparatoria. El navegador puede omitir la solicitud preparatoria si se cumplen todas las siguientes condiciones:
- El método de solicitud es GET, HE0AD o POST.
- La aplicación no establece encabezados de solicitud que no sean
Accept
,Accept-Language
,Content-Language
,Content-Type
oLast-Event-ID
. - El encabezado
Content-Type
, si está configurado, tiene uno de los siguientes valores:application/x-www-form-urlencoded
multipart/form-data
text/plain
La regla sobre encabezados de solicitud establecida para la solicitud del cliente se aplica a los encabezados que la aplicación establece llamando a setRequestHeader
en el objeto XMLHttpRequest
. La especificación CORS llama a estos encabezados de solicitud de autor. La regla no se aplica a los encabezados que el explorador puede establecer, como User-Agent
, Host
o Content-Length
.
La siguiente es una respuesta de ejemplo similar a la solicitud preparatoria realizada desde el botón [Put test] en la sección Test CORS de este documento.
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 solicitud preparatoria utiliza el método OPCIONES HTTP . Puede incluir los siguientes encabezados:
- Access-Control-Request-Method: método HTTP que se usará para la solicitud real.
- Access-Control-Request-Headers: una lista de encabezados de solicitud que la aplicación establece en la solicitud real. Como se indicó anteriormente, esto no incluye encabezados que establece el explorador, como
User-Agent
. - Métodos Access-Control-Allow
Si se rechaza la solicitud de preventa, la aplicación devuelve una respuesta 200 OK
pero no establece los encabezados CORS. Por lo tanto, el explorador no intenta la solicitud entre orígenes. Para obtener un ejemplo de una solicitud preparatoria denegada, consulte la sección Probar CORS de este documento.
Con las herramientas F12, la aplicación de consola muestra un error similar a uno de los siguientes, según el explorador:
- Firefox: solicitud de origen cruzado bloqueada: la política del mismo origen no permite leer el recurso remoto en
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Motivo: la solicitud CORS no se realizó correctamente). Más información - Chromium basado: la directiva de CORS ha bloqueado el acceso para capturar en "https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5" desde el origenhttps://cors3.azurewebsites.net: La respuesta a la solicitud preparatoria no pasa la comprobación de control de acceso: no hay ningún encabezado "Access-Control-Allow-Origin" presente en el recurso solicitado. Si una respuesta opaca sirve a sus necesidades, establezca el modo de la solicitud en 'no-cors' para obtener el recurso con CORS deshabilitado.
Para permitir encabezados específicos, llame a 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();
Para permitir todos los encabezados de solicitud de autor, llame a 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();
Los navegadores no son consistentes en la forma en que configuran Access-Control-Request-Headers
. Si alguna de las dos opciones:
- Los encabezados se establecen en cualquier otro valor que no sea
"*"
- AllowAnyHeader se llama a : incluya al menos
Accept
,Content-Type
yOrigin
, además de los encabezados personalizados que quiera admitir.
Código de solicitud de preparatoria automático
Cuando se aplica la directiva CORS:
- Globalmente llamando a
app.UseCors
enProgram.cs
. - Uso del atributo
[EnableCors]
.
ASP.NET Core responde a la solicitud preparatoria de OPTIONS.
Habilitar CORS por punto final usando RequireCors
actualmente no admite solicitudes automáticas preparatorias.
La sección CORS de prueba de este documento muestra este comportamiento.
Atributo [HttpOptions] para solicitudes preparatorias
Cuando CORS está habilitado con la directiva adecuada, ASP.NET Core suele responder automáticamente a las solicitudes previas de CORS. En algunos escenarios, es posible que esto no sea el caso. Por ejemplo, usando CORS con enrutamiento de punto final .
El código siguiente usa el atributo [HttpOptions] para crear puntos de conexión para las solicitudes 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);
}
Consulte Prueba CORS con enrutamiento de punto final y [HttpOptions] para obtener instrucciones sobre cómo probar el código anterior.
Establecer la hora de expiración previa
El encabezado Access-Control-Max-Age
especifica cuánto tiempo se puede almacenar en caché la respuesta a la solicitud preparatoria. Para establecer este encabezado, llame a 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();
Cómo funciona la CORS
Esta sección describe lo que sucede en una solicitud CORS a nivel de los mensajes HTTP.
- CORS no es una función de seguridad. El uso compartido de recursos entre orígenes (CORS) es una norma de W3C que permite mayor flexibilidad a los servidores en la directiva de mismo origen.
- Por ejemplo, un actor malicioso podría usar Cross-Site Scripting (XSS) contra su sitio y ejecutar una solicitud entre sitios a su sitio habilitado para CORS para robar información.
- Una API no es más segura al permitir CORS.
- Es necesario que el cliente (explorador) aplique CORS. El servidor ejecuta la solicitud y devuelve la respuesta, es el cliente que devuelve un error y bloquea la respuesta. Por ejemplo, cualquiera de las siguientes herramientas mostrará la respuesta del servidor:
- Fiddler
- .NET HttpClient
- Un explorador web escribiendo la dirección URL en la barra de direcciones.
- Es necesario que el cliente (explorador) aplique CORS. El servidor ejecuta la solicitud y devuelve la respuesta, es el cliente que devuelve un error y bloquea la respuesta. Por ejemplo, cualquiera de las siguientes herramientas mostrará la respuesta del servidor:
- Es una forma de que un servidor permita a los navegadores ejecutar una solicitud XHR o Fetch API de origen cruzado que, de lo contrario, estaría prohibida.
- Los navegadores sin CORS no pueden hacer solicitudes de origen cruzado. Antes de CORS, se usaba JSONP para eludir esta restricción. JSONP no utiliza XHR, utiliza la etiqueta
<script>
para recibir la respuesta. Se permite cargar scripts entre orígenes.
- Los navegadores sin CORS no pueden hacer solicitudes de origen cruzado. Antes de CORS, se usaba JSONP para eludir esta restricción. JSONP no utiliza XHR, utiliza la etiqueta
La especificación CORS introdujo varios encabezados HTTP nuevos que permiten solicitudes de origen cruzado. Si un explorador admite CORS, establece estos encabezados automáticamente para las solicitudes entre orígenes. El código JavaScript personalizado no es necesario para habilitar CORS.
El siguiente es un ejemplo de una solicitud de origen cruzado desde el botón de prueba Valores a https://cors1.azurewebsites.net/api/values
. El encabezado Origin
:
- Proporciona el dominio del sitio que realiza la solicitud.
- Es necesario y debe ser diferente del host.
Encabezados generales
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
Encabezados de respuesta
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
Encabezados de solicitud
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 ...
En las solicitudes OPTIONS
, el servidor establece el encabezado Encabezados de respuesta Access-Control-Allow-Origin: {allowed origin}
en la respuesta. Por ejemplo, la solicitud del botónejemplo, Eliminar [EnableCors]OPTIONS
desplegada contiene los siguientes encabezados:
Encabezados generales
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
Encabezados de respuesta
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
Encabezados de solicitud
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
En los encabezados de respuesta anteriores, el servidor establece el encabezado Access-Control-Allow-Origin en la respuesta. El valor https://cors1.azurewebsites.net
de este encabezado coincide con el encabezado Origin
de la solicitud.
Si se llama aAllowAnyOrigin, el Access-Control-Allow-Origin: *
se devuelve el valor comodín . AllowAnyOrigin
permite cualquier origen.
Si la respuesta no incluye el encabezado Access-Control-Allow-Origin
, la solicitud de origen cruzado falla. En concreto, el navegador no permite la solicitud. Incluso si el servidor devuelve una respuesta correcta, el explorador no hace que la respuesta esté disponible para la aplicación cliente.
El redireccionamiento HTTP a HTTPS provoca ERR_INVALID_REDIRECT en la solicitud de preparatoria de CORS
Las solicitudes a un punto de conexión usando HTTP que son redirigidas a HTTPS por UseHttpsRedirection fallan con ERR_INVALID_REDIRECT on the CORS preflight request
.
Los proyectos de API pueden rechazar solicitudes HTTP en lugar de usar UseHttpsRedirection
para redirigir solicitudes a HTTPS.
Mostrar solicitudes OPTIONS
De forma predeterminada, los exploradores Chrome y Edge no muestran las solicitudes OPTIONS en la pestaña de red de las herramientas F12. Para mostrar las solicitudes OPTIONS en estos exploradores:
chrome://flags/#out-of-blink-cors
oedge://flags/#out-of-blink-cors
- deshabilite la marca.
- Reiniciar.
Firefox muestra las solicitudes OPTIONS de forma predeterminada.
CORS en IIS
Al implementar en IIS, CORS debe ejecutarse antes de la autenticación de Windows si el servidor no está configurado para permitir el acceso anónimo. Para admitir este escenario, el módulo CORS de IIS debe instalarse y configurarse para la aplicación.
Prueba de CORS
La descarga de muestra tiene código para probar CORS. Vea cómo descargarlo. El ejemplo es un proyecto de API con Razor páginas agregadas:
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();
Advertencia
WithOrigins("https://localhost:<port>");
solo se debería usar para probar una aplicación de ejemplo similar al código de ejemplo de descarga.
A continuación ValuesController
se proporcionan los puntos de conexión para las pruebas:
[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();
}
El paquete de NuGet Rick.Docs.Samples.RouteInfo proporciona MyDisplayRouteInfo y se muestra la información de ruta.
Pruebe el código de ejemplo anterior mediante uno de los métodos siguientes:
- Ejecute el ejemplo con
dotnet run
utilizando la URL predeterminada dehttps://localhost:5001
. - Ejecute el ejemplo desde Visual Studio con el puerto establecido en 44398 para una URL de
https://localhost:44398
.
Uso de un explorador con las herramientas F12:
Seleccione el botón Valores y revise los encabezados en la pestaña Red.
Seleccione el botón probar PUT. Consulte Mostrar solicitudes de OPCIONES para obtener instrucciones sobre cómo mostrar la solicitud de OPCIONES. La prueba PUT crea dos solicitudes, una solicitud preparatoria de OPCIONES y la solicitud PUT.
Seleccione el
GetValues2 [DisableCors]
botón para desencadenar una solicitud CORS con error. Como se mencionó en el documento, la respuesta devuelve 200 correcto, pero no se realiza la solicitud CORS. Seleccione la pestaña Consola para ver el error de CORS. Dependiendo del navegador, se muestra un error similar al siguiente:El acceso a la obtención en
'https://cors1.azurewebsites.net/api/values/GetValues2'
desde el origen'https://cors3.azurewebsites.net'
ha sido bloqueado por la política de CORS: no hay encabezado 'Access-Control-Allow-Origin' presente en el recurso solicitado. Si una respuesta opaca sirve a sus necesidades, establezca el modo de la solicitud en 'no-cors' para obtener el recurso con CORS deshabilitado.
Los puntos finales habilitados para CORS se pueden probar con una herramienta, como curl o Fiddler. Cuando se utiliza una herramienta, el origen de la solicitud especificada por el encabezado Origin
debe diferir del host que recibe la solicitud. Si la solicitud no es de origen cruzado según el valor del encabezado Origin
:
- No es necesario que el middleware de CORS procese la solicitud.
- Los encabezados CORS no se devuelven en la respuesta.
El siguiente comando usa curl
para emitir una solicitud OPTIONS con información:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
Prueba de CORS con enrutamiento de puntos de conexión y [HttpOptions]
Habilitar CORS por punto final usando RequireCors
actualmente no admite solicitudes preparatorias automáticas . Considere el siguiente código que utiliza enrutamiento de punto final para habilitar 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();
A continuación TodoItems1Controller
se proporcionan los puntos de conexión para las pruebas:
[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);
}
Prueba el código anterior de la página de prueba (https://cors1.azurewebsites.net/test?number=1
) de la muestra implementada.
Los botones Delete [EnableCors] y GET [EnableCors] tienen éxito, porque los puntos finales tienen [EnableCors]
y responden a las solicitudes preparatorias. Se produce un error en los demás puntos de conexión. El botón GET falla, porque el JavaScript envía:
headers: {
"Content-Type": "x-custom-header"
},
A continuación TodoItems2Controller
se proporcionan puntos de conexión similares, pero se incluye código explícito para responder a las solicitudes 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);
}
El código anterior se puede probar al implementar la muestra en Azure. En la lista desplegable Controlador, selecciona Preparatorio y después Configurar controlador. Todas las llamadas CORS a los puntos finales TodoItems2Controller
tienen éxito.
Recursos adicionales
Por Rick Anderson y Kirk Larkin
En este artículo se muestra cómo habilitar CORS en una aplicación de ASP.NET Core.
La seguridad del explorador evita que una página web realice solicitudes a un dominio diferente del que atendió a dicha página web. Esta restricción se denomina directiva de mismo origen. La directiva de mismo origen evita que un sitio malintencionado lea información confidencial de otro sitio. A veces, es posible que quiera permitir que otros sitios realicen solicitudes entre orígenes a la aplicación. Para obtener más información, consulte el artículo CORS de Mozilla.
Intercambio de recursos de origen cruzado (CORS):
- Es una norma de W3C que permite mayor flexibilidad a los servidores en la directiva de mismo origen.
- No es una característica de seguridad, CORS relaja la seguridad. Una API no es más segura al permitir CORS. Para más información, vea Funcionamiento de CORS.
- Permite explícitamente a un servidor a permitir algunas solicitudes de origen cruzado y rechazar otras.
- Es más seguro y flexible que las técnicas anteriores, como JSONP.
Vea o descargue el código de ejemplo (cómo descargarlo)
Mismo origen
Dos URL tienen el mismo origen si tienen esquemas, hosts y puertos idénticos (RFC 6454).
Estas dos direcciones URL tienen el mismo origen:
https://example.com/foo.html
https://example.com/bar.html
Estas direcciones URL tienen orígenes diferentes a las dos direcciones URL anteriores:
https://example.net
: Dominio diferentehttps://www.example.com/foo.html
: Subdominio diferentehttp://example.com/foo.html
: Esquema diferentehttps://example.com:9000/foo.html
: Puerto diferente
Habilitación de CORS
Existen tres formas de habilitar CORS:
- En middleware mediante una directiva con nombre o directiva predeterminada .
- Utilizando el enrutamiento de punto de conexión.
- Con el atributo [EnableCors].
El uso del atributo [EnableCors] con una directiva con nombre proporciona el mejor control para limitar los puntos finales que admiten CORS.
Advertencia
UseCors debe llamarse en el orden correcto. Para obtener más información vea Orden de Middleware. Por ejemplo, UseCors
debe llamarse antes de UseResponseCaching cuando se utiliza UseResponseCaching
.
Cada ennfoque se detalla en las secciones siguientes.
CORS con directivas con nombre y middleware
El middleware de CORS controla las solicitudes entre orígenes. El código siguiente aplica una directiva de CORS a todos los puntos de conexión de la aplicación con los orígenes especificados:
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();
});
}
}
El código anterior:
- Establece el nombre de la directiva en
_myAllowSpecificOrigins
. El nombre de la política es arbitrario. - Llama al UseCorsmétodo de extensión y especifica la
_myAllowSpecificOrigins
directiva CORS.UseCors
agrega el middleware CORS. La llamada aUseCors
debe colocarse después deUseRouting
, pero antes deUseAuthorization
. Para obtener más información vea Orden de Middleware. - Llama a AddCors con una expresión lambda . La lambda toma un objeto CorsPolicyBuilder. Las opciones de configuración, como
WithOrigins
, se describen más adelante en este artículo. - Habilita la
_myAllowSpecificOrigins
directiva de CORS para todos los puntos de conexión del controlador. Consulte enrutamiento de punto final para aplicar una directiva CORS a puntos finales específicos. - Al usar middleware de almacenamiento en caché de respuesta, llame UseCors antes de UseResponseCaching.
Con el enrutamiento de puntos de conexión, el middleware de CORS debe configurarse para ejecutarse entre las llamadas a UseRouting
y UseEndpoints
.
Consulte Prueba CORS para obtener instrucciones sobre el código de prueba similar al código anterior.
La AddCors llamada al método añade servicios CORS al contenedor de servicios de la aplicación:
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();
}
Para obtener más información, consulte Opciones de directiva CORS en este documento.
Los CorsPolicyBuilder métodos se pueden encadenar, como se muestra en el código siguiente:
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();
}
Nota: La URL especificada no debe contener una barra diagonal (/
). Si la dirección URL finaliza con /
, la comparación devuelve false
y no se devuelve ningún encabezado.
CORS con directivas predeterminadas y middleware
El código resaltado siguiente habilita la directiva CORS predeterminada:
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();
});
}
}
El código anterior aplica la directiva CORS predeterminada a todos los puntos de conexión del controlador.
Habilitación de CORS con enrutamiento de punto de conexión
La habilitación de CORS por punto de conexión usando RequireCors
no admite solicitudes preparatorias automáticas. Para obtener más información, consulte este problema de GitHub y Pruebe CORS con enrutamiento de puntos de conexión y [HttpOptions].
Con el enrutamiento de punto final, CORS se puede habilitar por punto final utilizando el RequireCors conjunto de métodos de extensión:
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();
});
}
}
En el código anterior:
app.UseCors
agrega el middleware CORS. Debido a que no se ha configurado una política predeterminada,app.UseCors()
por sí solo no habilita CORS.- Los
/echo
y puntos de conexión del controlador permiten solicitudes entre orígenes mediante la directiva especificada. - Los
/echo2
y Razor puntos de conexión de páginas no permiten solicitudes de origen cruzado porque no se especificó ninguna política predeterminada.
El atributo [DisableCors]no deshabilita el CORS que ha sido habilitado por el enrutamiento de punto final con RequireCors
.
Consulte Prueba CORS con enrutamiento de punto final y [HttpOptions] para obtener instrucciones sobre el código de prueba similar al anterior.
Habilitación de CORS con atributos
Habilitar CORS con el atributo [EnableCors] y aplicar una directiva con nombre solo a los puntos de conexión que requieren CORS proporcionan el mejor control.
El atributo [EnableCors] proporciona una alternativa a aplicar CORS globalmente. El [EnableCors]
atributo habilita CORS para los puntos de conexión seleccionados, en lugar de todos los puntos de conexión:
[EnableCors]
especifica la política predeterminada.[EnableCors("{Policy String}")]
especifica una directiva guardada.
El atributo [EnableCors]
se puede aplicar a:
- Razor Página
PageModel
- Controlador
- Método de acción del controlador
Se pueden aplicar diferentes directivas a controladores, modelos de página o métodos de acción con el atributo [EnableCors]
. Cuando el atributo [EnableCors]
se aplica a un controlador, modelo de página o método de acción, y CORS está habilitado en middleware, se aplican ambas directivas. Se recomienda combinar directivas. Use el el atributo o middleware [EnableCors]
, no ambos, en la misma aplicación.
El código siguiente aplica una directiva diferente a cada método:
[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(),
};
}
}
El siguiente código crea dos políticas 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();
});
}
}
Para obtener el mejor control de la limitación de solicitudes CORS:
- Use
[EnableCors("MyPolicy")]
con una directiva con nombre. - No defina una directiva predeterminada.
- No utilice enrutamiento de punto final .
El código de la sección siguiente cumple la lista anterior.
Consulte Prueba CORS para obtener instrucciones sobre el código de prueba similar al código anterior.
Deshabilitación de CORS
El atributo [DisableCors]no deshabilita el CORS que ha sido habilitado por el enrutamiento de punto final .
El siguiente código define la política 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();
});
}
}
El código siguiente deshabilita CORS para la GetValues2
acción:
[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();
}
El código anterior:
- No habilita CORS con enrutamiento de punto final .
- No define una directiva CORS predeterminada.
- Usa [EnableCors("MyPolicy")] para habilitar la
"MyPolicy"
directiva CORS para el controlador. - Deshabilita CORS para el
GetValues2
método .
Consulte Probar CORS para obtener instrucciones sobre cómo probar el código anterior.
Opciones de directiva CORS
En esta sección se describen las distintas opciones que se pueden establecer en una directiva de CORS:
- Establecimiento de los orígenes permitidos
- Establecer los métodos HTTP permitidos
- Establecer los encabezados de solicitud permitidos
- Establecer los encabezados de respuesta expuestos
- Credenciales en solicitudes entre orígenes
- Establecer la hora de expiración previa
AddPolicy es llamado por Startup.ConfigureServices
. Para algunas opciones, puede resultar útil leer primero la sección Funcionamiento de CORS .
Establecimiento de los orígenes permitidos
AllowAnyOrigin: Permite solicitudes CORS desde todos los orígenes con cualquier esquema (http
o https
). AllowAnyOrigin
no es seguro porque cualquier sitio web puede realizar solicitudes entre orígenes a la aplicación.
Nota
Especificar AllowAnyOrigin
y AllowCredentials
es una configuración no segura y puede dar lugar a una falsificación de solicitud entre sitios. El servicio CORS devuelve una respuesta CORS no válida cuando una aplicación está configurada con ambos métodos.
AllowAnyOrigin
afecta a las solicitudes preparatorias y al Access-Control-Allow-Origin
encabezado. Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
SetIsOriginAllowedToAllowWildcardSubdomains: establece la IsOriginAllowed propiedad de la directiva en una función que permite que los orígenes coincidan con un dominio comodín configurado al evaluar si se permite el origen.
options.AddPolicy("MyAllowSubdomainPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
Establecimiento de los métodos HTTP permitidos
- Permite cualquier método HTTP:
- Afecta a las solicitudes preparatorias y al encabezado
Access-Control-Allow-Methods
. Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
Establecimiento de los encabezados de solicitud permitidos
Para permitir que se envíen encabezados específicos en una solicitud CORS, llamada encabezados de solicitud de autor , llame a WithHeaders y especifique los encabezados permitidos:
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
// requires using Microsoft.Net.Http.Headers;
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
Para permitir todos los encabezados de solicitud de autor, llame a AllowAnyHeader:
options.AddPolicy("MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
AllowAnyHeader
afecta a las solicitudes de preventa y al encabezado Access-Control-Request-Headers . Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
Una coincidencia de política de CORS Middleware con encabezados específicos especificados por WithHeaders
solo es posible cuando los encabezados enviados en Access-Control-Request-Headers
coinciden exactamente con los encabezados indicados en WithHeaders
.
Por ejemplo, considere la posibilidad de configurar una aplicación como se indica a continuación:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
CORS Middleware rechaza la solicitud preparatoria con el siguiente encabezado de solicitud porque Content-Language
(HeaderNames.ContentLanguage ) no aparece en WithHeaders
:
Access-Control-Request-Headers: Cache-Control, Content-Language
La aplicación devuelve una respuesta 200 OK pero no devuelve los encabezados CORS. Por lo tanto, el explorador no intenta la solicitud entre orígenes.
Establecer los encabezados de respuesta expuestos
De forma predeterminada, el explorador no expone todos los encabezados de respuesta a la aplicación. Para obtener más información, consulte W3C Cross-Origin Resource Sharing (Terminology): Simple Response Header.
Los encabezados de respuesta que están disponibles de forma predeterminada son:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
La especificación CORS llama a estos encabezados de respuesta simples. Para que otros encabezados estén disponibles para la aplicación, llame a WithExposedHeaders:
options.AddPolicy("MyExposeResponseHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.WithExposedHeaders("x-custom-header");
});
Credenciales en solicitudes entre orígenes
Las credenciales requieren un control especial en una solicitud CORS. De forma predeterminada, el navegador no envía credenciales con una solicitud de origen cruzado. Las credenciales incluyen cookies y esquemas de autenticación HTTP. Para enviar credenciales con una solicitud de origen cruzado, el cliente debe establecer XMLHttpRequest.withCredentials
en true
.
Uso XMLHttpRequest
directo:
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
Uso de jQuery:
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
Uso de la Fetch API:
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
El servidor debe permitir las credenciales. Para permitir credenciales de origen cruzado, llame a AllowCredentials:
options.AddPolicy("MyMyAllowCredentialsPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.AllowCredentials();
});
La respuesta HTTP incluye un encabezado Access-Control-Allow-Credentials
, que le dice al navegador que el servidor permite credenciales para una solicitud de origen cruzado.
Si el explorador envía credenciales, pero la respuesta no incluye un encabezado válido Access-Control-Allow-Credentials
, el explorador no expone la respuesta a la aplicación y se produce un error en la solicitud entre orígenes.
Permitir credenciales entre orígenes es un riesgo de seguridad. Un sitio web de otro dominio puede enviar las credenciales de un usuario que ha iniciado sesión a la aplicación en nombre del usuario sin el conocimiento del usuario.
La especificación CORS también indica que establecer orígenes en "*"
(todos los orígenes) no es válido si el Access-Control-Allow-Credentials
encabezado está presente.
Solicitud preparatoria
Para algunas solicitudes CORS, el navegador envía una solicitud adicional de OPTIONS antes de realizar la solicitud real. Esta solicitud se denomina solicitud preparatoria. El navegador puede omitir la solicitud preparatoria si se cumplen todas las siguientes condiciones:
- El método de solicitud es GET, HE0AD o POST.
- La aplicación no establece encabezados de solicitud que no sean
Accept
,Accept-Language
,Content-Language
,Content-Type
oLast-Event-ID
. - El encabezado
Content-Type
, si está configurado, tiene uno de los siguientes valores:application/x-www-form-urlencoded
multipart/form-data
text/plain
La regla sobre encabezados de solicitud establecida para la solicitud del cliente se aplica a los encabezados que la aplicación establece llamando a setRequestHeader
en el objeto XMLHttpRequest
. La especificación CORS llama a estos encabezados de solicitud de autor. La regla no se aplica a los encabezados que el explorador puede establecer, como User-Agent
, Host
o Content-Length
.
La siguiente es una respuesta de ejemplo similar a la solicitud preparatoria realizada desde el botón [Put test] en la sección Test CORS de este documento.
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 solicitud preparatoria utiliza el método OPCIONES HTTP . Puede incluir los siguientes encabezados:
- Access-Control-Request-Method: método HTTP que se usará para la solicitud real.
- Access-Control-Request-Headers: una lista de encabezados de solicitud que la aplicación establece en la solicitud real. Como se indicó anteriormente, esto no incluye encabezados que establece el explorador, como
User-Agent
. - Métodos Access-Control-Allow
Si se rechaza la solicitud de preventa, la aplicación devuelve una respuesta 200 OK
pero no establece los encabezados CORS. Por lo tanto, el explorador no intenta la solicitud entre orígenes. Para obtener un ejemplo de una solicitud preparatoria denegada, consulte la sección Probar CORS de este documento.
Con las herramientas F12, la aplicación de consola muestra un error similar a uno de los siguientes, según el explorador:
- Firefox: solicitud de origen cruzado bloqueada: la política del mismo origen no permite leer el recurso remoto en
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Motivo: la solicitud CORS no se realizó correctamente). Más información - Chromium basado: la directiva de CORS ha bloqueado el acceso para capturar en "https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5" desde el origenhttps://cors3.azurewebsites.net: La respuesta a la solicitud preparatoria no pasa la comprobación de control de acceso: no hay ningún encabezado "Access-Control-Allow-Origin" presente en el recurso solicitado. Si una respuesta opaca sirve a sus necesidades, establezca el modo de la solicitud en 'no-cors' para obtener el recurso con CORS deshabilitado.
Para permitir encabezados específicos, llame a WithHeaders:
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
// requires using Microsoft.Net.Http.Headers;
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
Para permitir todos los encabezados de solicitud de autor, llame a AllowAnyHeader:
options.AddPolicy("MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
Los navegadores no son consistentes en la forma en que configuran Access-Control-Request-Headers
. Si alguna de las dos opciones:
- Los encabezados se establecen en cualquier otro valor que no sea
"*"
- AllowAnyHeader se llama a : incluya al menos
Accept
,Content-Type
yOrigin
, además de los encabezados personalizados que quiera admitir.
Código de solicitud de preparatoria automático
Cuando se aplica la directiva CORS:
- Globalmente llamando a
app.UseCors
enStartup.Configure
. - Uso del atributo
[EnableCors]
.
ASP.NET Core responde a la solicitud preparatoria de OPTIONS.
Habilitar CORS por punto final usando RequireCors
actualmente no admite solicitudes automáticas preparatorias.
La sección CORS de prueba de este documento muestra este comportamiento.
Atributo [HttpOptions] para solicitudes preparatorias
Cuando CORS está habilitado con la directiva adecuada, ASP.NET Core suele responder automáticamente a las solicitudes previas de CORS. En algunos escenarios, es posible que esto no sea el caso. Por ejemplo, usando CORS con enrutamiento de punto final .
El código siguiente usa el atributo [HttpOptions] para crear puntos de conexión para las solicitudes 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);
}
Consulte Prueba CORS con enrutamiento de punto final y [HttpOptions] para obtener instrucciones sobre cómo probar el código anterior.
Establecer la hora de expiración previa
El encabezado Access-Control-Max-Age
especifica cuánto tiempo se puede almacenar en caché la respuesta a la solicitud preparatoria. Para establecer este encabezado, llame a SetPreflightMaxAge:
options.AddPolicy("MySetPreflightExpirationPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
});
Cómo funciona la CORS
Esta sección describe lo que sucede en una solicitud CORS a nivel de los mensajes HTTP.
- CORS no es una función de seguridad. El uso compartido de recursos entre orígenes (CORS) es una norma de W3C que permite mayor flexibilidad a los servidores en la directiva de mismo origen.
- Por ejemplo, un actor malicioso podría usar Cross-Site Scripting (XSS) contra su sitio y ejecutar una solicitud entre sitios a su sitio habilitado para CORS para robar información.
- Una API no es más segura al permitir CORS.
- Es necesario que el cliente (explorador) aplique CORS. El servidor ejecuta la solicitud y devuelve la respuesta, es el cliente que devuelve un error y bloquea la respuesta. Por ejemplo, cualquiera de las siguientes herramientas mostrará la respuesta del servidor:
- Fiddler
- .NET HttpClient
- Un explorador web escribiendo la dirección URL en la barra de direcciones.
- Es necesario que el cliente (explorador) aplique CORS. El servidor ejecuta la solicitud y devuelve la respuesta, es el cliente que devuelve un error y bloquea la respuesta. Por ejemplo, cualquiera de las siguientes herramientas mostrará la respuesta del servidor:
- Es una forma de que un servidor permita a los navegadores ejecutar una solicitud XHR o Fetch API de origen cruzado que, de lo contrario, estaría prohibida.
- Los navegadores sin CORS no pueden hacer solicitudes de origen cruzado. Antes de CORS, se usaba JSONP para eludir esta restricción. JSONP no utiliza XHR, utiliza la etiqueta
<script>
para recibir la respuesta. Se permite cargar scripts entre orígenes.
- Los navegadores sin CORS no pueden hacer solicitudes de origen cruzado. Antes de CORS, se usaba JSONP para eludir esta restricción. JSONP no utiliza XHR, utiliza la etiqueta
La especificación CORS introdujo varios encabezados HTTP nuevos que permiten solicitudes de origen cruzado. Si un explorador admite CORS, establece estos encabezados automáticamente para las solicitudes entre orígenes. El código JavaScript personalizado no es necesario para habilitar CORS.
El siguiente es un ejemplo de una solicitud de origen cruzado desde el botón de prueba Valores a https://cors1.azurewebsites.net/api/values
. El encabezado Origin
:
- Proporciona el dominio del sitio que realiza la solicitud.
- Es necesario y debe ser diferente del host.
Encabezados generales
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
Encabezados de respuesta
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
Encabezados de solicitud
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 ...
En las solicitudes OPTIONS
, el servidor establece el encabezado Encabezados de respuesta Access-Control-Allow-Origin: {allowed origin}
en la respuesta. Por ejemplo, la solicitud del botónejemplo, Eliminar [EnableCors]OPTIONS
desplegada contiene los siguientes encabezados:
Encabezados generales
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
Encabezados de respuesta
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
Encabezados de solicitud
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
En los encabezados de respuesta anteriores, el servidor establece el encabezado Access-Control-Allow-Origin en la respuesta. El valor https://cors1.azurewebsites.net
de este encabezado coincide con el encabezado Origin
de la solicitud.
Si se llama aAllowAnyOrigin, el Access-Control-Allow-Origin: *
se devuelve el valor comodín . AllowAnyOrigin
permite cualquier origen.
Si la respuesta no incluye el encabezado Access-Control-Allow-Origin
, la solicitud de origen cruzado falla. En concreto, el navegador no permite la solicitud. Incluso si el servidor devuelve una respuesta correcta, el explorador no hace que la respuesta esté disponible para la aplicación cliente.
Mostrar solicitudes OPTIONS
De forma predeterminada, los exploradores Chrome y Edge no muestran las solicitudes OPTIONS en la pestaña de red de las herramientas F12. Para mostrar las solicitudes OPTIONS en estos exploradores:
chrome://flags/#out-of-blink-cors
oedge://flags/#out-of-blink-cors
- deshabilite la marca.
- Reiniciar.
Firefox muestra las solicitudes OPTIONS de forma predeterminada.
CORS en IIS
Al implementar en IIS, CORS debe ejecutarse antes de la autenticación de Windows si el servidor no está configurado para permitir el acceso anónimo. Para admitir este escenario, el módulo CORS de IIS debe instalarse y configurarse para la aplicación.
Prueba de CORS
La descarga de muestra tiene código para probar CORS. Vea cómo descargarlo. El ejemplo es un proyecto de API con Razor páginas agregadas:
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();
});
}
}
Advertencia
WithOrigins("https://localhost:<port>");
solo se debería usar para probar una aplicación de ejemplo similar al código de ejemplo de descarga.
A continuación ValuesController
se proporcionan los puntos de conexión para las pruebas:
[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();
}
El paquete de NuGet Rick.Docs.Samples.RouteInfo proporciona MyDisplayRouteInfo y se muestra la información de ruta.
Pruebe el código de ejemplo anterior mediante uno de los métodos siguientes:
- Ejecute el ejemplo con
dotnet run
utilizando la URL predeterminada dehttps://localhost:5001
. - Ejecute el ejemplo desde Visual Studio con el puerto establecido en 44398 para una URL de
https://localhost:44398
.
Uso de un explorador con las herramientas F12:
Seleccione el botón Valores y revise los encabezados en la pestaña Red.
Seleccione el botón probar PUT. Consulte Mostrar solicitudes de OPCIONES para obtener instrucciones sobre cómo mostrar la solicitud de OPCIONES. La prueba PUT crea dos solicitudes, una solicitud preparatoria de OPCIONES y la solicitud PUT.
Seleccione el
GetValues2 [DisableCors]
botón para desencadenar una solicitud CORS con error. Como se mencionó en el documento, la respuesta devuelve 200 correcto, pero no se realiza la solicitud CORS. Seleccione la pestaña Consola para ver el error de CORS. Dependiendo del navegador, se muestra un error similar al siguiente:El acceso a la obtención en
'https://cors1.azurewebsites.net/api/values/GetValues2'
desde el origen'https://cors3.azurewebsites.net'
ha sido bloqueado por la política de CORS: no hay encabezado 'Access-Control-Allow-Origin' presente en el recurso solicitado. Si una respuesta opaca sirve a sus necesidades, establezca el modo de la solicitud en 'no-cors' para obtener el recurso con CORS deshabilitado.
Los puntos finales habilitados para CORS se pueden probar con una herramienta, como curl o Fiddler. Cuando se utiliza una herramienta, el origen de la solicitud especificada por el encabezado Origin
debe diferir del host que recibe la solicitud. Si la solicitud no es de origen cruzado según el valor del encabezado Origin
:
- No es necesario que el middleware de CORS procese la solicitud.
- Los encabezados CORS no se devuelven en la respuesta.
El siguiente comando usa curl
para emitir una solicitud OPTIONS con información:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
Prueba de CORS con enrutamiento de puntos de conexión y [HttpOptions]
Habilitar CORS por punto final usando RequireCors
actualmente no admite solicitudes preparatorias automáticas . Considere el siguiente código que utiliza enrutamiento de punto final para habilitar 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();
});
}
}
A continuación TodoItems1Controller
se proporcionan los puntos de conexión para las pruebas:
[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);
}
Prueba el código anterior de la página de prueba (https://cors1.azurewebsites.net/test?number=1
) de la muestra implementada.
Los botones Delete [EnableCors] y GET [EnableCors] tienen éxito, porque los puntos finales tienen [EnableCors]
y responden a las solicitudes preparatorias. Se produce un error en los demás puntos de conexión. El botón GET falla, porque el JavaScript envía:
headers: {
"Content-Type": "x-custom-header"
},
A continuación TodoItems2Controller
se proporcionan puntos de conexión similares, pero se incluye código explícito para responder a las solicitudes 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);
}
El código anterior se puede probar al implementar la muestra en Azure. En la lista desplegable Controlador, selecciona Preparatorio y después Configurar controlador. Todas las llamadas CORS a los puntos finales TodoItems2Controller
tienen éxito.