Partilhar via


Habilitar solicitações entre origens (CORS) no ASP.NET Core

Note

Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 10 deste artigo.

Warning

Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e do .NET Core. Para a versão atual, consulte a versão .NET 9 deste artigo.

Por Rick Anderson e Kirk Larkin

Este artigo mostra como Cross-Origin Resource Sharing (CORS) é ativado numa aplicação ASP.NET Core.

A segurança do navegador impede que uma página da Web faça solicitações para um domínio diferente daquele que serviu a página da Web. Essa restrição é chamada de política de mesma origem. A política de mesma origem impede que um site mal-intencionado leia dados confidenciais de outro site. Às vezes, você pode querer permitir que outros sites façam solicitações de origens cruzadas para seu aplicativo. Para obter mais informações, consulte o artigo do Mozilla CORS.

Compartilhamento de recursos entre origens (CORS):

  • É um padrão W3C que permite a um servidor flexibilizar a política de origem única.
  • Não é um recurso de segurança, o CORS relaxa a segurança. Uma API não é mais segura ao permitir CORS. Para obter mais informações, consulte Como funciona o CORS.
  • Permite que um servidor permita explicitamente algumas solicitações de origem cruzada enquanto rejeita outras.
  • É mais seguro e flexível do que as técnicas anteriores, como o JSONP.

Visualizar ou descarregar amostra de código (como descarregar)

Mesma origem

Dois URLs têm a mesma origem se tiverem esquemas, hosts e portas idênticos (RFC 6454).

Estes dois URLs têm a mesma origem:

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

Esses URLs têm origens diferentes dos dois URLs anteriores:

  • https://example.net: Domínio diferente
  • https://contoso.example.com/foo.html: Subdomínio diferente
  • http://example.com/foo.html: Esquema diferente
  • https://example.com:9000/foo.html: Porto diferente

Ativar CORS

Há três maneiras de habilitar o CORS:

Usar o atributo [EnableCors] com uma política nomeada fornece o melhor controle na limitação de pontos de extremidade que suportam CORS.

Warning

UseCors deve ser chamado na ordem correta. Para obter mais informações, consulte Ordem do middleware. Por exemplo, UseCors deve ser chamado antes de UseResponseCaching usar UseResponseCaching.

Cada abordagem é detalhada nas seções a seguir.

CORS com política nomeada e middleware

O CORS Middleware lida com solicitações de origens cruzadas. O seguinte código aplica uma política CORS a todos os endpoints da aplicação com as especificadas origens:

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

O código anterior:

Com o roteamento de endpoint, o middleware CORS deve ser configurado para ser executado entre as chamadas para UseRouting e UseEndpoints.

A AddCors chamada de método adiciona serviços CORS ao contêiner de serviço do aplicativo:

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 obter mais informações, consulte Opções de política CORS neste documento.

Os CorsPolicyBuilder métodos podem ser encadeados, como mostrado no código a seguir:

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: O URL especificado não deve conter uma barra final (/). Se a URL terminar com /, a comparação retornará false e nenhum cabeçalho será retornado.

Ordem de UseCors e UseStaticFiles

Normalmente, UseStaticFiles é chamado antes de UseCors. Os aplicativos que usam JavaScript para recuperar arquivos estáticos entre sites devem chamar UseCors antes de UseStaticFiles.

CORS com política padrão e middleware

O código realçado a seguir habilita a política CORS padrão:

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

O código anterior aplica a política CORS padrão a todos os pontos de extremidade do controlador.

Habilitar Cors com roteamento de ponto final

Com o roteamento de pontos finais, o CORS pode ser habilitado por ponto final usando o RequireCors conjunto de métodos de extensão:

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

No código anterior:

  • app.UseCors habilita o middleware CORS. Como uma política padrão não foi configurada, app.UseCors() sozinha não habilita o CORS.
  • Os /echo endpoints e controladores permitem solicitações de origem cruzada usando a política especificada.
  • Os endereços /echo2 e Razor Pages não permitem solicitações de origem cruzada porque nenhuma política padrão foi especificada.

O atributo [DisableCors]não desabilita o CORS que foi habilitado pelo roteamento de ponto de extremidade com RequireCors.

Consulte Test CORS with [EnableCors] attribute e RequireCors method para obter instruções sobre como testar código semelhante ao anterior.

Habilitar CORS com atributos

Habilitar CORS com o atributo [EnableCors] e aplicar uma política nomeada somente aos pontos de extremidade que exigem CORS fornece o melhor controle.

O atributo [EnableCors] fornece uma alternativa à aplicação global de CORS. O [EnableCors] atributo habilita CORS para pontos de extremidade selecionados, em vez de todos os pontos de extremidade:

  • [EnableCors] Especifica a política padrão.
  • [EnableCors("{Policy String}")] Especifica uma política nomeada.

O atributo [EnableCors] pode ser aplicado a:

  • Razor Página PageModel
  • Controller
  • Método de ação do controlador

Políticas diferentes podem ser aplicadas a controladores, modelos de página ou métodos de ação com o [EnableCors] atributo. Quando o atributo é aplicado a um controlador, modelo de página ou método de ação, e o [EnableCors] CORS é habilitado no middleware, ambas as políticas são aplicadas. Recomendamos que não se combine políticas. Utilize a seringa [EnableCors] atributo ou middleware, não ambos no mesmo aplicativo.

O código a seguir aplica uma política diferente para 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(),
        };
    }
}

O código a seguir cria duas 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 o melhor controle da limitação de solicitações CORS:

  • Utilize [EnableCors("MyPolicy")] com uma política nomeada.
  • Não defina uma política padrão.
  • Não use roteamento de ponto final.

O código na próxima seção atende à lista anterior.

Desativar CORS

O atributo [DisableCors]não desabilita o CORS que foi habilitado pelo roteamento de ponto de extremidade.

O código a seguir define a política "MyPolicy"CORS :

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

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

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

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

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

app.Run();

O código a seguir desabilita o CORS para a GetValues2 ação:

[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();

}

O código anterior:

Consulte Test CORS para obter instruções sobre como testar o código anterior.

Opções políticas do CORS

Esta seção descreve as várias opções que podem ser definidas em uma política CORS:

AddPolicy é chamado em Program.cs. Para algumas opções, pode ser útil ler primeiro a seção Como funciona o CORS .

Definir as origens permitidas

AllowAnyOrigin: Permite solicitações CORS de todas as origens com qualquer esquema (http ou https). AllowAnyOrigin é inseguro porque qualquer site pode fazer solicitações de origem cruzada para o aplicativo.

Note

Especificar AllowAnyOrigin e AllowCredentials é uma configuração insegura e pode resultar em falsificação de solicitação entre sites. O serviço CORS retorna uma resposta CORS inválida quando um aplicativo é configurado com ambos os métodos.

AllowAnyOrigin afeta as solicitações de pré-verificação e o cabeçalho Access-Control-Allow-Origin. Para obter mais informações, consulte a seção Solicitações Preflight.

SetIsOriginAllowedToAllowWildcardSubdomains: Define a IsOriginAllowed propriedade da política como uma função que permite que as origens correspondam a um domínio curinga configurado ao avaliar se a origem é permitida.

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

No código anterior, SetIsOriginAllowedToAllowWildcardSubdomains é chamado com a base de origem "https://example.com". Essa configuração permite solicitações CORS de qualquer subdomínio de example.com, como https://subdomain.example.com ou https://api.example.com. A correspondência de caracteres curinga é tratada pelo método, portanto, a origem deve ser especificada sem o caractere curinga * .

Definir os métodos HTTP permitidos

AllowAnyMethod:

  • Permite qualquer método HTTP:
  • Afecta as solicitações de comprovação e o Access-Control-Allow-Methods cabeçalho. Para obter mais informações, consulte a seção Solicitações Preflight.

Definir os cabeçalhos de solicitação permitidos

Para permitir que cabeçalhos específicos sejam enviados em uma solicitação CORS, chamados cabeçalhos de solicitação de autor, chame WithHeaders e especifique os cabeçalhos 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 os cabeçalhos de solicitação do autor, chame 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 afeta as solicitações preliminares e o cabeçalho Access-Control-Request-Headers. Para obter mais informações, consulte a seção Solicitações Preflight.

Uma correspondência de política de middleware CORS com cabeçalhos específicos especificados por WithHeaders só é possível quando os cabeçalhos enviados em Access-Control-Request-Headers correspondam exatamente aos cabeçalhos indicados em WithHeaders.

Por exemplo, considere um aplicativo configurado da seguinte maneira:

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

O CORS Middleware recusa um pedido preliminar com o seguinte cabeçalho de solicitação devido ao fato de que Content-Language (HeaderNames.ContentLanguage) não está listado em WithHeaders:

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

O aplicativo retorna uma resposta 200 OK , mas não envia os cabeçalhos CORS de volta. Portanto, o navegador não tenta a solicitação de origem cruzada.

Definir os cabeçalhos de resposta expostos

Por padrão, o navegador não expõe todos os cabeçalhos de resposta ao aplicativo. Para obter mais informações, consulte W3C Cross-Origin Resource Sharing (Terminology): Simple Response Header.

Os cabeçalhos de resposta disponíveis por padrão são:

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

A especificação CORS chama esses cabeçalhos de cabeçalhos de resposta simples. Para disponibilizar outros cabeçalhos para o aplicativo, chame 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();

Credenciais em solicitações de origem cruzada

As credenciais exigem tratamento especial em uma solicitação CORS. Por padrão, o navegador não envia credenciais com uma solicitação de origem cruzada. As credenciais incluem cookies e esquemas de autenticação HTTP. Para enviar credenciais com uma solicitação de origem cruzada, o cliente deve definir XMLHttpRequest.withCredentials como true.

Usando XMLHttpRequest diretamente:

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

Usando o jQuery:

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

Usando a Fetch API:

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

O servidor deve permitir as credenciais. Para permitir credenciais de origem cruzada, chame 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();

A resposta HTTP inclui um Access-Control-Allow-Credentials cabeçalho, que informa ao navegador que o servidor permite credenciais para uma solicitação de origem cruzada.

Se o navegador enviar credenciais, mas a resposta não incluir um cabeçalho válido Access-Control-Allow-Credentials , o navegador não exporá a resposta ao aplicativo e a solicitação de origem cruzada falhará.

Permitir credenciais de origem cruzada é um risco de segurança. Um site em outro domínio pode enviar as credenciais de um usuário conectado para o aplicativo em nome do usuário sem o conhecimento do usuário.

A especificação CORS também afirma que definir origens como "*" (todas as origens) é inválido se o Access-Control-Allow-Credentials cabeçalho estiver presente.

Pedidos de comprovação

Para algumas solicitações CORS, o navegador envia uma solicitação OPTIONS adicional antes de fazer a solicitação real. Essa solicitação é chamada de solicitação preliminar. O navegador pode ignorar a solicitação de comprovação se todas as seguintes condições forem verdadeiras:

  • O método de solicitação é GET, HEAD ou POST.
  • O aplicativo não define cabeçalhos de solicitação diferentes de Accept, Accept-Language, Content-Language, Content-Type, ou Last-Event-ID.
  • O Content-Type cabeçalho, se definido, tem um dos seguintes valores:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

A regra relativa aos cabeçalhos de solicitação definidos para a solicitação do cliente aplica-se aos cabeçalhos que o aplicativo define ao chamar setRequestHeader no objeto XMLHttpRequest. A especificação CORS chama esses cabeçalhos de cabeçalhos de solicitação de autor. A regra não se aplica a cabeçalhos que o navegador pode definir, como User-Agent, Hostou Content-Length.

Note

Este artigo contém URLs criadas pela implantação do código de exemplo em dois sites https://cors3.azurewebsites.net do Azure e https://cors.azurewebsites.net.

A seguir está um exemplo de resposta semelhante ao pedido de preflight feito a partir do botão [Colocar Teste] na seção Test CORS deste 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

O pedido preflight usa o método HTTP OPTIONS. Pode incluir os seguintes cabeçalhos:

  • Access-Control-Request-Method: O método HTTP que será usado para a solicitação real.
  • Access-Control-Request-Headers: uma lista de cabeçalhos de solicitação que o aplicativo define na solicitação real. Como dito anteriormente, isso não inclui cabeçalhos que o navegador define, como User-Agent.

Se a solicitação de pre-verificação for negada, o aplicativo retornará uma 200 OK resposta, mas não definirá os cabeçalhos CORS. Portanto, o navegador não tenta a solicitação de origem cruzada. Para obter um exemplo de um pedido preflight negado, consulte a seção Test CORS deste documento.

Usando as ferramentas F12, o aplicativo de console mostra um erro semelhante a um dos seguintes, dependendo do navegador:

  • Firefox: Cross-Origin Request Blocked: A mesma política de origem não permite a leitura do recurso remoto em https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Motivo: o pedido do CORS não teve sucesso). Saiba mais
  • Baseado no Chromium: O acesso para buscar em 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' da origem 'https://cors3.azurewebsites.net' foi bloqueado pela política CORS: A resposta à solicitação de pré-vôo não passa na verificação de controle de acesso: Nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Se uma resposta opaca atender às suas necessidades, defina o modo da solicitação como 'no-cors' para buscar o recurso com o CORS desativado.

Para permitir cabeçalhos específicos, chame 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 os cabeçalhos de solicitação do autor, chame 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();

Os navegadores não são consistentes na maneira como definem Access-Control-Request-Headers. Em caso afirmativo:

  • Os cabeçalhos são definidos para qualquer coisa diferente de "*"
  • AllowAnyHeader é chamado: Inclua pelo menos Accept, Content-Type, e Origin, além de quaisquer cabeçalhos personalizados que deseje suportar.

Código automático de pedido de comprovação

Quando a política CORS é aplicada, quer seja de uma forma ou de outra:

  • Globalmente, ligando para app.UseCors em Program.cs.
  • Usando o [EnableCors] atributo.

ASP.NET Core responde à solicitação OPTIONS de pré-verificação.

A seção Test CORS deste documento demonstra esse comportamento.

Atributo [HttpOptions] para pedidos de pré-verificação

Quando o CORS é ativado com a política apropriada, o ASP.NET Core geralmente responde automaticamente às solicitações preflight de CORS.

O código a seguir usa o atributo [HttpOptions] para criar pontos de extremidade para solicitações 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 Test CORS with [EnableCors] attribute e RequireCors method para obter instruções sobre como testar o código anterior.

Definir o tempo de expiração da verificação prévia

O Access-Control-Max-Age cabeçalho especifica quanto tempo a resposta para a requisição preflight pode ser armazenada em cache. Para definir esse cabeçalho, chame 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();

Ativar CORS num ponto final

Como funciona o CORS

Esta seção descreve o que acontece em uma solicitação CORS no nível das mensagens HTTP.

  • O CORS não é um elemento de segurança. O CORS é um padrão W3C que permite que um servidor relaxe a política de mesma origem.
    • Por exemplo, um ator mal-intencionado pode usar Cross-Site Scripting (XSS) no seu site e executar uma solicitação entre sites para um site com CORS ativado, a fim de roubar informações.
  • Uma API não é mais segura ao permitir CORS.
    • Cabe ao cliente (navegador) aplicar o CORS. O servidor executa a solicitação e retorna a resposta, é o cliente que retorna um erro e bloqueia a resposta. Por exemplo, qualquer uma das seguintes ferramentas exibirá a resposta do servidor:
  • É uma maneira de um servidor permitir que os navegadores executem um pedido XHR ou Fetch API entre origens cruzadas que de outra forma seria proibido.
    • Navegadores sem CORS não podem fazer solicitações de origens cruzadas. Antes do CORS, o JSONP era usado para contornar essa restrição. JSONP não usa XHR, ele usa a <script> tag para receber a resposta. Os scripts podem ser carregados entre origens.

A especificação CORS introduziu vários novos cabeçalhos HTTP que permitem solicitações de origens cruzadas. Se um navegador suportar CORS, ele define esses cabeçalhos automaticamente para solicitações de origem cruzada. O código JavaScript personalizado não é necessário para ativar o CORS.

A seguir está um exemplo de uma solicitação de origem cruzada do botão de teste Valores para https://cors1.azurewebsites.net/api/values. O Origin cabeçalho:

  • Fornece o domínio do site que está fazendo a solicitação.
  • É obrigatório e deve ser diferente do servidor.

Cabeçalhos gerais

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

Cabeçalhos de resposta

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

Cabeçalhos de solicitação

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

Nas OPTIONS solicitações, o servidor define o cabeçalho de respostaAccess-Control-Allow-Origin: {allowed origin} na resposta. Por exemplo, no código de exemplo, a solicitação de Delete [EnableCors] botão OPTIONS contém os seguintes cabeçalhos:

Cabeçalhos gerais

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

Cabeçalhos de resposta

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

Cabeçalhos de solicitação

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

Nos cabeçalhos de resposta anteriores, o servidor define o cabeçalho Access-Control-Allow-Origin na resposta. O https://cors1.azurewebsites.net valor desse cabeçalho corresponde ao Origin cabeçalho da solicitação.

Se AllowAnyOrigin for chamado, o Access-Control-Allow-Origin: *, o valor curinga, é retornado. AllowAnyOrigin permite qualquer origem.

Se a resposta não incluir o Access-Control-Allow-Origin cabeçalho, a solicitação de origem cruzada falhará. Especificamente, o navegador não permite a solicitação. Mesmo que o servidor retorne uma resposta bem-sucedida, o navegador não disponibilizará a resposta para o aplicativo cliente.

O redirecionamento HTTP para HTTPS causa ERR_INVALID_REDIRECT na requisição de pré-verificação CORS

Solicitações para um ponto de extremidade utilizando HTTP que são redirecionadas para HTTPS por UseHttpsRedirection falham com ERR_INVALID_REDIRECT on the CORS preflight request.

Os projetos de API podem rejeitar solicitações HTTP em vez de usar UseHttpsRedirection para redirecionar solicitações para HTTPS.

CORS no IIS

Ao implantar no IIS, o CORS deve ser executado antes da Autenticação do Windows se o servidor não estiver configurado para permitir acesso anônimo. Para dar suporte a esse cenário, o módulo CORS do IIS precisa ser instalado e configurado para o aplicativo.

Teste CORS

O download de exemplo tem código para testar o CORS. Veja como fazer o download. O exemplo é um projeto de API com páginas Razor adicionadas:

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

Warning

WithOrigins("https://localhost:<port>"); só deve ser usado para testar um aplicativo de exemplo semelhante ao código de exemplo de download.

Note

Se você estiver usando launchSettings.json no Visual Studio ou definindo configurações de depuração em C# no VS Code e usando o IIS Express para depurar localmente, certifique-se de ter configurado o IIS Express para "anonymousAuthentication": true. Quando "anonymousAuthentication" for false, o host do ambiente Web ASP.NET Core não verá nenhuma solicitação de comprovação. Em particular, se você estiver usando a autenticação NTLM ("windowsAuthentication": true), a primeira etapa do desafio-resposta NTLM é enviar ao navegador da Web um desafio 401, o que pode tornar difícil verificar se sua rota de comprovação está configurada corretamente.

O seguinte ValuesController fornece os endereços para teste:

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

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

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


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

}

MyDisplayRouteInfo é fornecido pelo pacote Rick.Docs.Samples.RouteInfo NuGet e exibe informações de rota.

Teste o código de exemplo anterior usando uma das seguintes abordagens:

  • Execute o exemplo com dotnet run usando a URL padrão de https://localhost:5001.
  • Execute o exemplo do Visual Studio com a porta definida como 44398 para uma URL de https://localhost:44398.

Usando um navegador com as ferramentas F12:

  • Selecione o botão Valores e revise os cabeçalhos na guia Rede .

  • Selecione o botão de teste PUT . Consulte Exibir solicitações OPTIONS para obter instruções sobre como exibir a solicitação OPTIONS. O teste PUT cria duas solicitações: uma solicitação preflight OPTIONS e uma solicitação PUT.

  • Selecione o GetValues2 [DisableCors] botão para acionar uma solicitação CORS com falha. Como mencionado no documento, a resposta retorna o código 200 de sucesso, mas a requisição CORS não é feita. Selecione a guia Console para ver o erro CORS. Dependendo do navegador, um erro semelhante ao seguinte é exibido:

    O acesso ao recurso em 'https://cors1.azurewebsites.net/api/values/GetValues2' da origem 'https://cors3.azurewebsites.net' foi bloqueado pela política CORS: nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Se uma resposta opaca atender às suas necessidades, defina o modo da solicitação como 'no-cors' para buscar o recurso com o CORS desativado.

Os endpoints habilitados para CORS podem ser testados com uma ferramenta, como curl ou Fiddler. Ao usar uma ferramenta, a origem da solicitação especificada pelo Origin cabeçalho deve ser diferente do host que recebe a solicitação. Se a solicitação não for de origem cruzada com base no valor do Origin cabeçalho:

  • Não há necessidade de o CORS Middleware processar a solicitação.
  • Os cabeçalhos CORS não são retornados na resposta.

O comando a seguir é usado curl para emitir uma solicitação OPTIONS com informações:

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

Teste CORS com o atributo [EnableCors] e o método RequireCors

Considere o código a seguir que usa o roteamento de ponto de extremidade para habilitar o CORS por ponto final usando 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 apenas o /echo ponto de extremidade está usando o RequireCors para permitir solicitações de origem cruzada usando a política especificada. Os controladores abaixo habilitam o CORS usando o atributo [EnableCors].

O seguinte TodoItems1Controller fornece endpoints para testes:

[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);
}

Os botões Delete [EnableCors] e GET [EnableCors] são bem-sucedidos, porque os endpoints têm [EnableCors] e respondem a pedidos preflight. Os outros pontos finais falham. O botão GET falha, porque o JavaScript envia:

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

O seguinte TodoItems2Controller fornece endpoints semelhantes, mas inclui código explícito para responder a pedidos 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);
}

O código anterior pode ser testado implantando o exemplo no Azure. Na lista suspensa Controller, selecione Preflight e, em seguida, Set Controller. Todas as chamadas CORS para os TodoItems2Controller endpoints são realizadas com sucesso.

Recursos adicionais

Por Rick Anderson e Kirk Larkin

Este artigo mostra como habilitar o CORS em um aplicativo ASP.NET Core.

A segurança do navegador impede que uma página da Web faça solicitações para um domínio diferente daquele que serviu a página da Web. Essa restrição é chamada de política de mesma origem. A política de mesma origem impede que um site mal-intencionado leia dados confidenciais de outro site. Às vezes, você pode querer permitir que outros sites façam solicitações de origens cruzadas para seu aplicativo. Para obter mais informações, consulte o artigo do Mozilla CORS.

Compartilhamento de recursos entre origens (CORS):

  • É um padrão W3C que permite a um servidor flexibilizar a política de origem única.
  • Não é um recurso de segurança, o CORS relaxa a segurança. Uma API não é mais segura ao permitir CORS. Para obter mais informações, consulte Como funciona o CORS.
  • Permite que um servidor permita explicitamente algumas solicitações de origem cruzada enquanto rejeita outras.
  • É mais seguro e flexível do que as técnicas anteriores, como o JSONP.

Visualizar ou descarregar amostra de código (como descarregar)

Mesma origem

Dois URLs têm a mesma origem se tiverem esquemas, hosts e portas idênticos (RFC 6454).

Estes dois URLs têm a mesma origem:

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

Esses URLs têm origens diferentes dos dois URLs anteriores:

  • https://example.net: Domínio diferente
  • https://www.example.com/foo.html: Subdomínio diferente
  • http://example.com/foo.html: Esquema diferente
  • https://example.com:9000/foo.html: Porto diferente

Ativar CORS

Há três maneiras de habilitar o CORS:

Usar o atributo [EnableCors] com uma política nomeada fornece o melhor controle na limitação de pontos de extremidade que suportam CORS.

Warning

UseCors deve ser chamado na ordem correta. Para obter mais informações, consulte Ordem do middleware. Por exemplo, UseCors deve ser chamado antes de UseResponseCaching usar UseResponseCaching.

Cada abordagem é detalhada nas seções a seguir.

CORS com política nomeada e middleware

O CORS Middleware lida com solicitações de origens cruzadas. O seguinte código aplica uma política CORS a todos os endpoints da aplicação com as especificadas origens:

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

O código anterior:

Com o roteamento de endpoint, o middleware CORS deve ser configurado para ser executado entre as chamadas para UseRouting e UseEndpoints.

A AddCors chamada de método adiciona serviços CORS ao contêiner de serviço do aplicativo:

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 obter mais informações, consulte Opções de política CORS neste documento.

Os CorsPolicyBuilder métodos podem ser encadeados, como mostrado no código a seguir:

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: O URL especificado não deve conter uma barra final (/). Se a URL terminar com /, a comparação retornará false e nenhum cabeçalho será retornado.

Warning

UseCors deve ser colocado depois UseRouting e antes de UseAuthorization. Isso é para garantir que os cabeçalhos CORS sejam incluídos na resposta para chamadas autorizadas e não autorizadas.

Ordem de UseCors e UseStaticFiles

Normalmente, UseStaticFiles é chamado antes de UseCors. Os aplicativos que usam JavaScript para recuperar arquivos estáticos entre sites devem chamar UseCors antes de UseStaticFiles.

CORS com política padrão e middleware

O código realçado a seguir habilita a política CORS padrão:

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

O código anterior aplica a política CORS padrão a todos os pontos de extremidade do controlador.

Habilitar Cors com roteamento de ponto final

Com o roteamento de pontos finais, o CORS pode ser habilitado por ponto final usando o RequireCors conjunto de métodos de extensão:

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

No código anterior:

  • app.UseCors habilita o middleware CORS. Como uma política padrão não foi configurada, app.UseCors() sozinha não habilita o CORS.
  • Os /echo endpoints e controladores permitem solicitações de origem cruzada usando a política especificada.
  • Os endereços /echo2 e Razor Pages não permitem solicitações de origem cruzada porque nenhuma política padrão foi especificada.

O atributo [DisableCors]não desabilita o CORS que foi habilitado pelo roteamento de ponto de extremidade com RequireCors.

No .NET 7, o atributo [EnableCors] deve passar um parâmetro, caso contrário, será gerado um aviso ASP0023 devido a uma correspondência ambígua na rota. O .NET 8 ou posterior não gera o ASP0023 aviso.

[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 Test CORS with [EnableCors] attribute e RequireCors method para obter instruções sobre como testar código semelhante ao anterior.

Habilitar CORS com atributos

Habilitar CORS com o atributo [EnableCors] e aplicar uma política nomeada somente aos pontos de extremidade que exigem CORS fornece o melhor controle.

O atributo [EnableCors] fornece uma alternativa à aplicação global de CORS. O [EnableCors] atributo habilita CORS para pontos de extremidade selecionados, em vez de todos os pontos de extremidade:

  • [EnableCors] Especifica a política padrão.
  • [EnableCors("{Policy String}")] Especifica uma política nomeada.

O atributo [EnableCors] pode ser aplicado a:

  • Razor Página PageModel
  • Controller
  • Método de ação do controlador

Políticas diferentes podem ser aplicadas a controladores, modelos de página ou métodos de ação com o [EnableCors] atributo. Quando o atributo é aplicado a um controlador, modelo de página ou método de ação, e o [EnableCors] CORS é habilitado no middleware, ambas as políticas são aplicadas. Recomendamos que não se combine políticas. Utilize a seringa [EnableCors] atributo ou middleware, não ambos no mesmo aplicativo.

O código a seguir aplica uma política diferente para 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(),
        };
    }
}

O código a seguir cria duas 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 o melhor controle da limitação de solicitações CORS:

  • Utilize [EnableCors("MyPolicy")] com uma política nomeada.
  • Não defina uma política padrão.
  • Não use roteamento de ponto final.

O código na próxima seção atende à lista anterior.

Desativar CORS

O atributo [DisableCors]não desabilita o CORS que foi habilitado pelo roteamento de ponto de extremidade.

O código a seguir define a política "MyPolicy"CORS :

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

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

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

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

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

app.Run();

O código a seguir desabilita o CORS para a GetValues2 ação:

[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();

}

O código anterior:

Consulte Test CORS para obter instruções sobre como testar o código anterior.

Opções políticas do CORS

Esta seção descreve as várias opções que podem ser definidas em uma política CORS:

AddPolicy é chamado em Program.cs. Para algumas opções, pode ser útil ler primeiro a seção Como funciona o CORS .

Definir as origens permitidas

AllowAnyOrigin: Permite solicitações CORS de todas as origens com qualquer esquema (http ou https). AllowAnyOrigin é inseguro porque qualquer site pode fazer solicitações de origem cruzada para o aplicativo.

Note

Especificar AllowAnyOrigin e AllowCredentials é uma configuração insegura e pode resultar em falsificação de solicitação entre sites. O serviço CORS retorna uma resposta CORS inválida quando um aplicativo é configurado com ambos os métodos.

AllowAnyOrigin afeta as solicitações de pré-verificação e o cabeçalho Access-Control-Allow-Origin. Para obter mais informações, consulte a seção Solicitações Preflight.

SetIsOriginAllowedToAllowWildcardSubdomains: Define a IsOriginAllowed propriedade da política como uma função que permite que as origens correspondam a um domínio curinga configurado ao avaliar se a origem é permitida.

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

No código anterior, SetIsOriginAllowedToAllowWildcardSubdomains é chamado com a base de origem "https://example.com". Essa configuração permite solicitações CORS de qualquer subdomínio de example.com, como https://subdomain.example.com ou https://api.example.com. A correspondência de caracteres curinga é tratada pelo método, portanto, a origem deve ser especificada sem o caractere curinga * .

Definir os métodos HTTP permitidos

AllowAnyMethod:

  • Permite qualquer método HTTP:
  • Afecta as solicitações de comprovação e o Access-Control-Allow-Methods cabeçalho. Para obter mais informações, consulte a seção Solicitações Preflight.

Definir os cabeçalhos de solicitação permitidos

Para permitir que cabeçalhos específicos sejam enviados em uma solicitação CORS, chamados cabeçalhos de solicitação de autor, chame WithHeaders e especifique os cabeçalhos 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 os cabeçalhos de solicitação do utilizador, chame 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 afeta as solicitações preliminares e o cabeçalho Access-Control-Request-Headers. Para obter mais informações, consulte a seção Solicitações Preflight.

Uma correspondência de política de middleware CORS com cabeçalhos específicos especificados por WithHeaders só é possível quando os cabeçalhos enviados em Access-Control-Request-Headers correspondam exatamente aos cabeçalhos indicados em WithHeaders.

Por exemplo, considere um aplicativo configurado da seguinte maneira:

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

O CORS Middleware recusa um pedido preliminar com o seguinte cabeçalho de solicitação devido ao fato de que Content-Language (HeaderNames.ContentLanguage) não está listado em WithHeaders:

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

O aplicativo retorna uma resposta 200 OK , mas não envia os cabeçalhos CORS de volta. Portanto, o navegador não tenta a solicitação de origem cruzada.

Definir os cabeçalhos de resposta expostos

Por padrão, o navegador não expõe todos os cabeçalhos de resposta ao aplicativo. Para obter mais informações, consulte W3C Cross-Origin Resource Sharing (Terminology): Simple Response Header.

Os cabeçalhos de resposta disponíveis por padrão são:

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

A especificação CORS chama esses cabeçalhos de cabeçalhos de resposta simples. Para disponibilizar outros cabeçalhos para o aplicativo, chame 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();

Credenciais em solicitações de origem cruzada

As credenciais exigem tratamento especial em uma solicitação CORS. Por padrão, o navegador não envia credenciais com uma solicitação de origem cruzada. As credenciais incluem cookies e esquemas de autenticação HTTP. Para enviar credenciais com uma solicitação de origem cruzada, o cliente deve definir XMLHttpRequest.withCredentials como true.

Usando XMLHttpRequest diretamente:

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

Usando o jQuery:

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

Usando a Fetch API:

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

O servidor deve permitir as credenciais. Para permitir credenciais de origem cruzada, chame 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();

A resposta HTTP inclui um Access-Control-Allow-Credentials cabeçalho, que informa ao navegador que o servidor permite credenciais para uma solicitação de origem cruzada.

Se o navegador enviar credenciais, mas a resposta não incluir um cabeçalho válido Access-Control-Allow-Credentials , o navegador não exporá a resposta ao aplicativo e a solicitação de origem cruzada falhará.

Permitir credenciais de origem cruzada é um risco de segurança. Um site em outro domínio pode enviar as credenciais de um usuário conectado para o aplicativo em nome do usuário sem o conhecimento do usuário.

A especificação CORS também afirma que definir origens como "*" (todas as origens) é inválido se o Access-Control-Allow-Credentials cabeçalho estiver presente.

Pedidos de comprovação

Para algumas solicitações CORS, o navegador envia uma solicitação OPTIONS adicional antes de fazer a solicitação real. Essa solicitação é chamada de solicitação preliminar. O navegador pode ignorar a solicitação de comprovação se todas as seguintes condições forem verdadeiras:

  • O método de solicitação é GET, HEAD ou POST.
  • O aplicativo não define cabeçalhos de solicitação diferentes de Accept, Accept-Language, Content-Language, Content-Type, ou Last-Event-ID.
  • O Content-Type cabeçalho, se definido, tem um dos seguintes valores:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

A regra relativa aos cabeçalhos de solicitação definidos para a solicitação do cliente aplica-se aos cabeçalhos que o aplicativo define ao chamar setRequestHeader no objeto XMLHttpRequest. A especificação CORS chama esses cabeçalhos de cabeçalhos de solicitação de autor. A regra não se aplica a cabeçalhos que o navegador pode definir, como User-Agent, Hostou Content-Length.

A seguir está um exemplo de resposta semelhante ao pedido de preflight feito a partir do botão [Colocar Teste] na seção Test CORS deste 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

O pedido preflight usa o método HTTP OPTIONS. Pode incluir os seguintes cabeçalhos:

Se a solicitação de pre-verificação for negada, o aplicativo retornará uma 200 OK resposta, mas não definirá os cabeçalhos CORS. Portanto, o navegador não tenta a solicitação de origem cruzada. Para obter um exemplo de um pedido preflight negado, consulte a seção Test CORS deste documento.

Usando as ferramentas F12, o aplicativo de console mostra um erro semelhante a um dos seguintes, dependendo do navegador:

  • Firefox: Cross-Origin Request Blocked: A mesma política de origem não permite a leitura do recurso remoto em https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Motivo: o pedido do CORS não teve sucesso). Saiba mais
  • Baseado no Chromium: O acesso para buscar em 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' da origem 'https://cors3.azurewebsites.net' foi bloqueado pela política CORS: A resposta à solicitação de pré-vôo não passa na verificação de controle de acesso: Nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Se uma resposta opaca atender às suas necessidades, defina o modo da solicitação como 'no-cors' para buscar o recurso com o CORS desativado.

Para permitir cabeçalhos específicos, chame 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 os cabeçalhos de solicitação do utilizador, chame 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();

Os navegadores não são consistentes na maneira como definem Access-Control-Request-Headers. Em caso afirmativo:

  • Os cabeçalhos são definidos para qualquer coisa diferente de "*"
  • AllowAnyHeader é chamado: Inclua pelo menos Accept, Content-Type, e Origin, além de quaisquer cabeçalhos personalizados que deseje suportar.

Código automático de pedido de comprovação

Quando a política CORS é aplicada, quer seja de uma forma ou de outra:

  • Globalmente, ligando para app.UseCors em Program.cs.
  • Usando o [EnableCors] atributo.

ASP.NET Core responde à solicitação OPTIONS de pré-verificação.

A seção Test CORS deste documento demonstra esse comportamento.

Atributo [HttpOptions] para pedidos de pré-verificação

Quando o CORS é ativado com a política apropriada, o ASP.NET Core geralmente responde automaticamente às solicitações preflight de CORS.

O código a seguir usa o atributo [HttpOptions] para criar pontos de extremidade para solicitações 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 Test CORS with [EnableCors] attribute e RequireCors method para obter instruções sobre como testar o código anterior.

Definir o tempo de expiração da verificação prévia

O Access-Control-Max-Age cabeçalho especifica quanto tempo a resposta para a requisição preflight pode ser armazenada em cache. Para definir esse cabeçalho, chame 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();

Ativar CORS num ponto final

Como funciona o CORS

Esta seção descreve o que acontece em uma solicitação CORS no nível das mensagens HTTP.

  • O CORS não é um elemento de segurança. O CORS é um padrão W3C que permite que um servidor relaxe a política de mesma origem.
    • Por exemplo, um ator mal-intencionado pode usar Cross-Site Scripting (XSS) no seu site e executar uma solicitação entre sites para um site com CORS ativado, a fim de roubar informações.
  • Uma API não é mais segura ao permitir CORS.
    • Cabe ao cliente (navegador) aplicar o CORS. O servidor executa a solicitação e retorna a resposta, é o cliente que retorna um erro e bloqueia a resposta. Por exemplo, qualquer uma das seguintes ferramentas exibirá a resposta do servidor:
  • É uma maneira de um servidor permitir que os navegadores executem um pedido XHR ou Fetch API entre origens cruzadas que de outra forma seria proibido.
    • Navegadores sem CORS não podem fazer solicitações de origens cruzadas. Antes do CORS, o JSONP era usado para contornar essa restrição. JSONP não usa XHR, ele usa a <script> tag para receber a resposta. Os scripts podem ser carregados entre origens.

A especificação CORS introduziu vários novos cabeçalhos HTTP que permitem solicitações de origens cruzadas. Se um navegador suportar CORS, ele define esses cabeçalhos automaticamente para solicitações de origem cruzada. O código JavaScript personalizado não é necessário para ativar o CORS.

Selecione o botão de teste PUT no exemplo implantado. O Origin cabeçalho:

  • Fornece o domínio do site que está fazendo a solicitação.
  • É obrigatório e deve ser diferente do servidor.

Cabeçalhos gerais

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

Cabeçalhos de resposta

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

Cabeçalhos de solicitação

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

Nas OPTIONS solicitações, o servidor define o cabeçalho de respostaAccess-Control-Allow-Origin: {allowed origin} na resposta. Por exemplo, no código de exemplo, a solicitação de Delete [EnableCors] botão OPTIONS contém os seguintes cabeçalhos:

Cabeçalhos gerais

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

Cabeçalhos de resposta

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

Cabeçalhos de solicitação

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

Nos cabeçalhos de resposta anteriores, o servidor define o cabeçalho Access-Control-Allow-Origin na resposta. O https://cors1.azurewebsites.net valor desse cabeçalho corresponde ao Origin cabeçalho da solicitação.

Se AllowAnyOrigin for chamado, o Access-Control-Allow-Origin: *, o valor curinga, é retornado. AllowAnyOrigin permite qualquer origem.

Se a resposta não incluir o Access-Control-Allow-Origin cabeçalho, a solicitação de origem cruzada falhará. Especificamente, o navegador não permite a solicitação. Mesmo que o servidor retorne uma resposta bem-sucedida, o navegador não disponibilizará a resposta para o aplicativo cliente.

O redirecionamento HTTP para HTTPS causa ERR_INVALID_REDIRECT na requisição de pré-verificação CORS

Solicitações para um ponto de extremidade utilizando HTTP que são redirecionadas para HTTPS por UseHttpsRedirection falham com ERR_INVALID_REDIRECT on the CORS preflight request.

Os projetos de API podem rejeitar solicitações HTTP em vez de usar UseHttpsRedirection para redirecionar solicitações para HTTPS.

CORS no IIS

Ao implantar no IIS, o CORS deve ser executado antes da Autenticação do Windows se o servidor não estiver configurado para permitir acesso anônimo. Para dar suporte a esse cenário, o módulo CORS do IIS precisa ser instalado e configurado para o aplicativo.

Teste CORS

O download de exemplo tem código para testar o CORS. Veja como fazer o download. O exemplo é um projeto de API com páginas Razor adicionadas:

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

Warning

WithOrigins("https://localhost:<port>"); só deve ser usado para testar um aplicativo de exemplo semelhante ao código de exemplo de download.

O seguinte ValuesController fornece os endereços para teste:

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

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

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


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

}

MyDisplayRouteInfo é fornecido pelo pacote Rick.Docs.Samples.RouteInfo NuGet e exibe informações de rota.

Teste o código de exemplo anterior usando uma das seguintes abordagens:

  • Execute o exemplo com dotnet run usando a URL padrão de https://localhost:5001.
  • Execute o exemplo do Visual Studio com a porta definida como 44398 para uma URL de https://localhost:44398.

Usando um navegador com as ferramentas F12:

  • Selecione o botão Valores e revise os cabeçalhos na guia Rede .

  • Selecione o botão de teste PUT . Consulte Exibir solicitações OPTIONS para obter instruções sobre como exibir a solicitação OPTIONS. O teste PUT cria duas solicitações: uma solicitação preflight OPTIONS e uma solicitação PUT.

  • Selecione o GetValues2 [DisableCors] botão para acionar uma solicitação CORS com falha. Como mencionado no documento, a resposta retorna o código 200 de sucesso, mas a requisição CORS não é feita. Selecione a guia Console para ver o erro CORS. Dependendo do navegador, um erro semelhante ao seguinte é exibido:

    O acesso ao recurso em 'https://cors1.azurewebsites.net/api/values/GetValues2' da origem 'https://cors3.azurewebsites.net' foi bloqueado pela política CORS: nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Se uma resposta opaca atender às suas necessidades, defina o modo da solicitação como 'no-cors' para buscar o recurso com o CORS desativado.

Os endpoints habilitados para CORS podem ser testados com uma ferramenta, como curl ou Fiddler. Ao usar uma ferramenta, a origem da solicitação especificada pelo Origin cabeçalho deve ser diferente do host que recebe a solicitação. Se a solicitação não for de origem cruzada com base no valor do Origin cabeçalho:

  • Não há necessidade de o CORS Middleware processar a solicitação.
  • Os cabeçalhos CORS não são retornados na resposta.

O comando a seguir é usado curl para emitir uma solicitação OPTIONS com informações:

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

Teste CORS com o atributo [EnableCors] e o método RequireCors

Considere o código a seguir que usa o roteamento de ponto de extremidade para habilitar o CORS por ponto final usando 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 apenas o /echo ponto de extremidade está usando o RequireCors para permitir solicitações de origem cruzada usando a política especificada. Os controladores abaixo habilitam o CORS usando o atributo [EnableCors].

O seguinte TodoItems1Controller fornece endpoints para testes:

[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);
}

Os botões Delete [EnableCors] e GET [EnableCors] são bem-sucedidos, porque os endpoints têm [EnableCors] e respondem a pedidos preflight. Os outros pontos finais falham. O botão GET falha, porque o JavaScript envia:

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

O seguinte TodoItems2Controller fornece endpoints semelhantes, mas inclui código explícito para responder a pedidos 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);
}

O código anterior pode ser testado ao implantar o exemplo no Azure. Na lista suspensa Controlador, selecione Pré-voo e, em seguida, Definir controlador. Todas as chamadas CORS para os TodoItems2Controller endpoints são realizadas com sucesso.

Recursos adicionais

Por Rick Anderson e Kirk Larkin

Este artigo mostra como habilitar o CORS em um aplicativo ASP.NET Core.

A segurança do navegador impede que uma página da Web faça solicitações para um domínio diferente daquele que serviu a página da Web. Essa restrição é chamada de política de mesma origem. A política de mesma origem impede que um site mal-intencionado leia dados confidenciais de outro site. Às vezes, você pode querer permitir que outros sites façam solicitações de origens cruzadas para seu aplicativo. Para obter mais informações, consulte o artigo do Mozilla CORS.

Compartilhamento de recursos entre origens (CORS):

  • É um padrão W3C que permite a um servidor flexibilizar a política de origem única.
  • Não é um recurso de segurança, o CORS relaxa a segurança. Uma API não é mais segura ao permitir CORS. Para obter mais informações, consulte Como funciona o CORS.
  • Permite que um servidor permita explicitamente algumas solicitações de origem cruzada enquanto rejeita outras.
  • É mais seguro e flexível do que as técnicas anteriores, como o JSONP.

Visualizar ou descarregar amostra de código (como descarregar)

Mesma origem

Dois URLs têm a mesma origem se tiverem esquemas, hosts e portas idênticos (RFC 6454).

Estes dois URLs têm a mesma origem:

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

Esses URLs têm origens diferentes dos dois URLs anteriores:

  • https://example.net: Domínio diferente
  • https://www.example.com/foo.html: Subdomínio diferente
  • http://example.com/foo.html: Esquema diferente
  • https://example.com:9000/foo.html: Porto diferente

Ativar CORS

Há três maneiras de habilitar o CORS:

Usar o atributo [EnableCors] com uma política nomeada fornece o melhor controle na limitação de pontos de extremidade que suportam CORS.

Warning

UseCors deve ser chamado na ordem correta. Para obter mais informações, consulte Ordem do middleware. Por exemplo, UseCors deve ser chamado antes de UseResponseCaching usar UseResponseCaching.

Cada abordagem é detalhada nas seções a seguir.

CORS com política nomeada e middleware

O CORS Middleware lida com solicitações de origens cruzadas. O seguinte código aplica uma política CORS a todos os endpoints da aplicação com as especificadas origens:

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

O código anterior:

Com o roteamento de endpoint, o middleware CORS deve ser configurado para ser executado entre as chamadas para UseRouting e UseEndpoints.

A AddCors chamada de método adiciona serviços CORS ao contêiner de serviço do aplicativo:

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 obter mais informações, consulte Opções de política CORS neste documento.

Os CorsPolicyBuilder métodos podem ser encadeados, como mostrado no código a seguir:

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: O URL especificado não deve conter uma barra final (/). Se a URL terminar com /, a comparação retornará false e nenhum cabeçalho será retornado.

Warning

UseCors deve ser colocado depois UseRouting e antes de UseAuthorization. Isso é para garantir que os cabeçalhos CORS sejam incluídos na resposta para chamadas autorizadas e não autorizadas.

Ordem de UseCors e UseStaticFiles

Normalmente, UseStaticFiles é chamado antes de UseCors. Os aplicativos que usam JavaScript para recuperar arquivos estáticos entre sites devem chamar UseCors antes de UseStaticFiles.

CORS com política padrão e middleware

O código realçado a seguir habilita a política CORS padrão:

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

O código anterior aplica a política CORS padrão a todos os pontos de extremidade do controlador.

Habilitar Cors com roteamento de ponto final

A ativação do CORS por endpoint não RequireCorssuporta solicitações automáticas de preflight. Para obter mais informações, consulte este problema do GitHub e Teste o CORS com roteamento por endpoint e [HttpOptions].

Com o roteamento de pontos finais, o CORS pode ser habilitado por ponto final usando o RequireCors conjunto de métodos de extensão:

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

No código anterior:

  • app.UseCors habilita o middleware CORS. Como uma política padrão não foi configurada, app.UseCors() sozinha não habilita o CORS.
  • Os /echo endpoints e controladores permitem solicitações de origem cruzada usando a política especificada.
  • Os endereços /echo2 e Razor Pages não permitem solicitações de origem cruzada porque nenhuma política padrão foi especificada.

O atributo [DisableCors]não desabilita o CORS que foi habilitado pelo roteamento de ponto de extremidade com RequireCors.

Consulte Testar CORS com roteamento de ponto de extremidade e [HttpOptions] para obter instruções sobre como testar código semelhante ao anterior.

Habilitar CORS com atributos

Habilitar CORS com o atributo [EnableCors] e aplicar uma política nomeada somente aos pontos de extremidade que exigem CORS fornece o melhor controle.

O atributo [EnableCors] fornece uma alternativa à aplicação global de CORS. O [EnableCors] atributo habilita CORS para pontos de extremidade selecionados, em vez de todos os pontos de extremidade:

  • [EnableCors] Especifica a política padrão.
  • [EnableCors("{Policy String}")] Especifica uma política nomeada.

O atributo [EnableCors] pode ser aplicado a:

  • Razor Página PageModel
  • Controller
  • Método de ação do controlador

Políticas diferentes podem ser aplicadas a controladores, modelos de página ou métodos de ação com o [EnableCors] atributo. Quando o atributo é aplicado a um controlador, modelo de página ou método de ação, e o [EnableCors] CORS é habilitado no middleware, ambas as políticas são aplicadas. Recomendamos que não se combine políticas. Utilize a seringa [EnableCors] atributo ou middleware, não ambos no mesmo aplicativo.

O código a seguir aplica uma política diferente para 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(),
        };
    }
}

O código a seguir cria duas 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 o melhor controle da limitação de solicitações CORS:

  • Utilize [EnableCors("MyPolicy")] com uma política nomeada.
  • Não defina uma política padrão.
  • Não use roteamento de ponto final.

O código na próxima seção atende à lista anterior.

Desativar CORS

O atributo [DisableCors]não desabilita o CORS que foi habilitado pelo roteamento de ponto de extremidade.

O código a seguir define a política "MyPolicy"CORS :

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

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

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

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

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

app.Run();

O código a seguir desabilita o CORS para a GetValues2 ação:

[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();

}

O código anterior:

Consulte Test CORS para obter instruções sobre como testar o código anterior.

Opções políticas do CORS

Esta seção descreve as várias opções que podem ser definidas em uma política CORS:

AddPolicy é chamado em Program.cs. Para algumas opções, pode ser útil ler primeiro a seção Como funciona o CORS .

Definir as origens permitidas

AllowAnyOrigin: Permite solicitações CORS de todas as origens com qualquer esquema (http ou https). AllowAnyOrigin é inseguro porque qualquer site pode fazer solicitações de origem cruzada para o aplicativo.

Note

Especificar AllowAnyOrigin e AllowCredentials é uma configuração insegura e pode resultar em falsificação de solicitação entre sites. O serviço CORS retorna uma resposta CORS inválida quando um aplicativo é configurado com ambos os métodos.

AllowAnyOrigin afeta as solicitações de pré-verificação e o cabeçalho Access-Control-Allow-Origin. Para obter mais informações, consulte a seção Solicitações Preflight.

SetIsOriginAllowedToAllowWildcardSubdomains: Define a IsOriginAllowed propriedade da política como uma função que permite que as origens correspondam a um domínio curinga configurado ao avaliar se a origem é permitida.

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

No código anterior, SetIsOriginAllowedToAllowWildcardSubdomains é chamado com a base de origem "https://example.com". Essa configuração permite solicitações CORS de qualquer subdomínio de example.com, como https://subdomain.example.com ou https://api.example.com. A correspondência de caracteres curinga é tratada pelo método, portanto, a origem deve ser especificada sem o caractere curinga * .

Definir os métodos HTTP permitidos

AllowAnyMethod:

  • Permite qualquer método HTTP:
  • Afecta as solicitações de comprovação e o Access-Control-Allow-Methods cabeçalho. Para obter mais informações, consulte a seção Solicitações Preflight.

Definir os cabeçalhos de solicitação permitidos

Para permitir que cabeçalhos específicos sejam enviados em uma solicitação CORS, chamados cabeçalhos de solicitação de autor, chame WithHeaders e especifique os cabeçalhos 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 os cabeçalhos de solicitação do utilizador, chame 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 afeta as solicitações preliminares e o cabeçalho Access-Control-Request-Headers. Para obter mais informações, consulte a seção Solicitações Preflight.

Uma correspondência de política de middleware CORS com cabeçalhos específicos especificados por WithHeaders só é possível quando os cabeçalhos enviados em Access-Control-Request-Headers correspondam exatamente aos cabeçalhos indicados em WithHeaders.

Por exemplo, considere um aplicativo configurado da seguinte maneira:

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

O CORS Middleware recusa um pedido preliminar com o seguinte cabeçalho de solicitação devido ao fato de que Content-Language (HeaderNames.ContentLanguage) não está listado em WithHeaders:

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

O aplicativo retorna uma resposta 200 OK , mas não envia os cabeçalhos CORS de volta. Portanto, o navegador não tenta a solicitação de origem cruzada.

Definir os cabeçalhos de resposta expostos

Por padrão, o navegador não expõe todos os cabeçalhos de resposta ao aplicativo. Para obter mais informações, consulte W3C Cross-Origin Resource Sharing (Terminology): Simple Response Header.

Os cabeçalhos de resposta disponíveis por padrão são:

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

A especificação CORS chama esses cabeçalhos de cabeçalhos de resposta simples. Para disponibilizar outros cabeçalhos para o aplicativo, chame 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();

Credenciais em solicitações de origem cruzada

As credenciais exigem tratamento especial em uma solicitação CORS. Por padrão, o navegador não envia credenciais com uma solicitação de origem cruzada. As credenciais incluem cookies e esquemas de autenticação HTTP. Para enviar credenciais com uma solicitação de origem cruzada, o cliente deve definir XMLHttpRequest.withCredentials como true.

Usando XMLHttpRequest diretamente:

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

Usando o jQuery:

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

Usando a Fetch API:

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

O servidor deve permitir as credenciais. Para permitir credenciais de origem cruzada, chame 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();

A resposta HTTP inclui um Access-Control-Allow-Credentials cabeçalho, que informa ao navegador que o servidor permite credenciais para uma solicitação de origem cruzada.

Se o navegador enviar credenciais, mas a resposta não incluir um cabeçalho válido Access-Control-Allow-Credentials , o navegador não exporá a resposta ao aplicativo e a solicitação de origem cruzada falhará.

Permitir credenciais de origem cruzada é um risco de segurança. Um site em outro domínio pode enviar as credenciais de um usuário conectado para o aplicativo em nome do usuário sem o conhecimento do usuário.

A especificação CORS também afirma que definir origens como "*" (todas as origens) é inválido se o Access-Control-Allow-Credentials cabeçalho estiver presente.

Pedidos de comprovação

Para algumas solicitações CORS, o navegador envia uma solicitação OPTIONS adicional antes de fazer a solicitação real. Essa solicitação é chamada de solicitação preliminar. O navegador pode ignorar a solicitação de comprovação se todas as seguintes condições forem verdadeiras:

  • O método de solicitação é GET, HEAD ou POST.
  • O aplicativo não define cabeçalhos de solicitação diferentes de Accept, Accept-Language, Content-Language, Content-Type, ou Last-Event-ID.
  • O Content-Type cabeçalho, se definido, tem um dos seguintes valores:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

A regra relativa aos cabeçalhos de solicitação definidos para a solicitação do cliente aplica-se aos cabeçalhos que o aplicativo define ao chamar setRequestHeader no objeto XMLHttpRequest. A especificação CORS chama esses cabeçalhos de cabeçalhos de solicitação de autor. A regra não se aplica a cabeçalhos que o navegador pode definir, como User-Agent, Hostou Content-Length.

A seguir está um exemplo de resposta semelhante ao pedido de preflight feito a partir do botão [Colocar Teste] na seção Test CORS deste 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

O pedido preflight usa o método HTTP OPTIONS. Pode incluir os seguintes cabeçalhos:

Se a solicitação de pre-verificação for negada, o aplicativo retornará uma 200 OK resposta, mas não definirá os cabeçalhos CORS. Portanto, o navegador não tenta a solicitação de origem cruzada. Para obter um exemplo de um pedido preflight negado, consulte a seção Test CORS deste documento.

Usando as ferramentas F12, o aplicativo de console mostra um erro semelhante a um dos seguintes, dependendo do navegador:

  • Firefox: Cross-Origin Request Blocked: A mesma política de origem não permite a leitura do recurso remoto em https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Motivo: o pedido do CORS não teve sucesso). Saiba mais
  • Baseado no Chromium: O acesso para buscar em 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' da origem 'https://cors3.azurewebsites.net' foi bloqueado pela política CORS: A resposta à solicitação de pré-vôo não passa na verificação de controle de acesso: Nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Se uma resposta opaca atender às suas necessidades, defina o modo da solicitação como 'no-cors' para buscar o recurso com o CORS desativado.

Para permitir cabeçalhos específicos, chame 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 os cabeçalhos de solicitação do utilizador, chame 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();

Os navegadores não são consistentes na maneira como definem Access-Control-Request-Headers. Em caso afirmativo:

  • Os cabeçalhos são definidos para qualquer coisa diferente de "*"
  • AllowAnyHeader é chamado: Inclua pelo menos Accept, Content-Type, e Origin, além de quaisquer cabeçalhos personalizados que deseje suportar.

Código automático de pedido de comprovação

Quando a política CORS é aplicada, quer seja de uma forma ou de outra:

  • Globalmente, ligando para app.UseCors em Program.cs.
  • Usando o [EnableCors] atributo.

ASP.NET Core responde à solicitação OPTIONS de pré-verificação.

Habilitar o CORS por endpoint usando RequireCors atualmente não suporta solicitações automáticas de pré-verificação.

A seção Test CORS deste documento demonstra esse comportamento.

Atributo [HttpOptions] para pedidos de pré-verificação

Quando o CORS é ativado com a política apropriada, o ASP.NET Core geralmente responde automaticamente às solicitações preflight de CORS. Em alguns cenários, pode não ser esse o caso. Por exemplo, usando CORS com roteamento de ponto final.

O código a seguir usa o atributo [HttpOptions] para criar pontos de extremidade para solicitações 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 Testar CORS com roteamento de ponto de extremidade e [HttpOptions] para obter instruções sobre como testar o código anterior.

Definir o tempo de expiração da verificação prévia

O Access-Control-Max-Age cabeçalho especifica quanto tempo a resposta para a requisição preflight pode ser armazenada em cache. Para definir esse cabeçalho, chame 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();

Como funciona o CORS

Esta seção descreve o que acontece em uma solicitação CORS no nível das mensagens HTTP.

  • O CORS não é um elemento de segurança. O CORS é um padrão W3C que permite que um servidor relaxe a política de mesma origem.
    • Por exemplo, um ator mal-intencionado pode usar Cross-Site Scripting (XSS) no seu site e executar uma solicitação entre sites para um site com CORS ativado, a fim de roubar informações.
  • Uma API não é mais segura ao permitir CORS.
    • Cabe ao cliente (navegador) aplicar o CORS. O servidor executa a solicitação e retorna a resposta, é o cliente que retorna um erro e bloqueia a resposta. Por exemplo, qualquer uma das seguintes ferramentas exibirá a resposta do servidor:
  • É uma maneira de um servidor permitir que os navegadores executem um pedido XHR ou Fetch API entre origens cruzadas que de outra forma seria proibido.
    • Navegadores sem CORS não podem fazer solicitações de origens cruzadas. Antes do CORS, o JSONP era usado para contornar essa restrição. JSONP não usa XHR, ele usa a <script> tag para receber a resposta. Os scripts podem ser carregados entre origens.

A especificação CORS introduziu vários novos cabeçalhos HTTP que permitem solicitações de origens cruzadas. Se um navegador suportar CORS, ele define esses cabeçalhos automaticamente para solicitações de origem cruzada. O código JavaScript personalizado não é necessário para ativar o CORS.

A seguir está um exemplo de uma solicitação de origem cruzada do botão de teste Valores para https://cors1.azurewebsites.net/api/values. O Origin cabeçalho:

  • Fornece o domínio do site que está fazendo a solicitação.
  • É obrigatório e deve ser diferente do servidor.

Cabeçalhos gerais

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

Cabeçalhos de resposta

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

Cabeçalhos de solicitação

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

Nas OPTIONS solicitações, o servidor define o cabeçalho de respostaAccess-Control-Allow-Origin: {allowed origin} na resposta. Por exemplo, a amostra implantada, a solicitação do botão OPTIONS Excluir contém os seguintes cabeçalhos:

Cabeçalhos gerais

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

Cabeçalhos de resposta

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

Cabeçalhos de solicitação

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

Nos cabeçalhos de resposta anteriores, o servidor define o cabeçalho Access-Control-Allow-Origin na resposta. O https://cors1.azurewebsites.net valor desse cabeçalho corresponde ao Origin cabeçalho da solicitação.

Se AllowAnyOrigin for chamado, o Access-Control-Allow-Origin: *, o valor curinga, é retornado. AllowAnyOrigin permite qualquer origem.

Se a resposta não incluir o Access-Control-Allow-Origin cabeçalho, a solicitação de origem cruzada falhará. Especificamente, o navegador não permite a solicitação. Mesmo que o servidor retorne uma resposta bem-sucedida, o navegador não disponibilizará a resposta para o aplicativo cliente.

O redirecionamento HTTP para HTTPS causa ERR_INVALID_REDIRECT na requisição de pré-verificação CORS

Solicitações para um ponto de extremidade utilizando HTTP que são redirecionadas para HTTPS por UseHttpsRedirection falham com ERR_INVALID_REDIRECT on the CORS preflight request.

Os projetos de API podem rejeitar solicitações HTTP em vez de usar UseHttpsRedirection para redirecionar solicitações para HTTPS.

Exibir solicitações OPÇÕES

Por padrão, os navegadores Chrome e Edge não mostram solicitações OPTIONS no separador de rede, das ferramentas F12. Para exibir solicitações OPTIONS nestes navegadores:

  • chrome://flags/#out-of-blink-cors ou edge://flags/#out-of-blink-cors
  • Desative o sinalizador.
  • restart.

O Firefox mostra solicitações de OPÇÕES por padrão.

CORS no IIS

Ao implantar no IIS, o CORS deve ser executado antes da Autenticação do Windows se o servidor não estiver configurado para permitir acesso anônimo. Para dar suporte a esse cenário, o módulo CORS do IIS precisa ser instalado e configurado para o aplicativo.

Teste CORS

O download de exemplo tem código para testar o CORS. Veja como fazer o download. O exemplo é um projeto de API com páginas Razor adicionadas:

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

Warning

WithOrigins("https://localhost:<port>"); só deve ser usado para testar um aplicativo de exemplo semelhante ao código de exemplo de download.

O seguinte ValuesController fornece os endereços para teste:

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

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

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


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

}

MyDisplayRouteInfo é fornecido pelo pacote Rick.Docs.Samples.RouteInfo NuGet e exibe informações de rota.

Teste o código de exemplo anterior usando uma das seguintes abordagens:

  • Execute o exemplo com dotnet run usando a URL padrão de https://localhost:5001.
  • Execute o exemplo do Visual Studio com a porta definida como 44398 para uma URL de https://localhost:44398.

Usando um navegador com as ferramentas F12:

  • Selecione o botão Valores e revise os cabeçalhos na guia Rede .

  • Selecione o botão de teste PUT . Consulte Exibir solicitações OPTIONS para obter instruções sobre como exibir a solicitação OPTIONS. O teste PUT cria duas solicitações: uma solicitação preflight OPTIONS e uma solicitação PUT.

  • Selecione o GetValues2 [DisableCors] botão para acionar uma solicitação CORS com falha. Como mencionado no documento, a resposta retorna o código 200 de sucesso, mas a requisição CORS não é feita. Selecione a guia Console para ver o erro CORS. Dependendo do navegador, um erro semelhante ao seguinte é exibido:

    O acesso ao recurso em 'https://cors1.azurewebsites.net/api/values/GetValues2' da origem 'https://cors3.azurewebsites.net' foi bloqueado pela política CORS: nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Se uma resposta opaca atender às suas necessidades, defina o modo da solicitação como 'no-cors' para buscar o recurso com o CORS desativado.

Os endpoints habilitados para CORS podem ser testados com uma ferramenta, como curl ou Fiddler. Ao usar uma ferramenta, a origem da solicitação especificada pelo Origin cabeçalho deve ser diferente do host que recebe a solicitação. Se a solicitação não for de origem cruzada com base no valor do Origin cabeçalho:

  • Não há necessidade de o CORS Middleware processar a solicitação.
  • Os cabeçalhos CORS não são retornados na resposta.

O comando a seguir é usado curl para emitir uma solicitação OPTIONS com informações:

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

Teste CORS com roteamento de ponto final e [HttpOptions]

Habilitar o CORS em uma base por endpoint usando RequireCors atualmente não suporta pedidos preflight automáticos. Considere o seguinte código que utiliza o roteamento de endpoints para ativar o 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();

O seguinte TodoItems1Controller fornece endpoints para testes:

[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);
}

Teste o código precedente na página de teste (https://cors1.azurewebsites.net/test?number=1) do exemplo implementado.

Os botões Delete [EnableCors] e GET [EnableCors] são bem-sucedidos, porque os endpoints têm [EnableCors] e respondem a pedidos preflight. Os outros pontos finais falham. O botão GET falha, porque o JavaScript envia:

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

O seguinte TodoItems2Controller fornece endpoints semelhantes, mas inclui código explícito para responder a pedidos 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);
}

O código anterior pode ser testado ao implantar o exemplo no Azure. Na lista suspensa Controlador, selecione Pré-voo e, em seguida, Definir controlador. Todas as chamadas CORS para os TodoItems2Controller endpoints são realizadas com sucesso.

Recursos adicionais

Por Rick Anderson e Kirk Larkin

Este artigo mostra como habilitar o CORS em um aplicativo ASP.NET Core.

A segurança do navegador impede que uma página da Web faça solicitações para um domínio diferente daquele que serviu a página da Web. Essa restrição é chamada de política de mesma origem. A política de mesma origem impede que um site mal-intencionado leia dados confidenciais de outro site. Às vezes, você pode querer permitir que outros sites façam solicitações de origens cruzadas para seu aplicativo. Para obter mais informações, consulte o artigo do Mozilla CORS.

Compartilhamento de recursos entre origens (CORS):

  • É um padrão W3C que permite a um servidor flexibilizar a política de origem única.
  • Não é um recurso de segurança, o CORS relaxa a segurança. Uma API não é mais segura ao permitir CORS. Para obter mais informações, consulte Como funciona o CORS.
  • Permite que um servidor permita explicitamente algumas solicitações de origem cruzada enquanto rejeita outras.
  • É mais seguro e flexível do que as técnicas anteriores, como o JSONP.

Visualizar ou descarregar amostra de código (como descarregar)

Mesma origem

Dois URLs têm a mesma origem se tiverem esquemas, hosts e portas idênticos (RFC 6454).

Estes dois URLs têm a mesma origem:

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

Esses URLs têm origens diferentes dos dois URLs anteriores:

  • https://example.net: Domínio diferente
  • https://www.example.com/foo.html: Subdomínio diferente
  • http://example.com/foo.html: Esquema diferente
  • https://example.com:9000/foo.html: Porto diferente

Ativar CORS

Há três maneiras de habilitar o CORS:

Usar o atributo [EnableCors] com uma política nomeada fornece o melhor controle na limitação de pontos de extremidade que suportam CORS.

Warning

UseCors deve ser chamado na ordem correta. Para obter mais informações, consulte Ordem do middleware. Por exemplo, UseCors deve ser chamado antes de UseResponseCaching usar UseResponseCaching.

Cada abordagem é detalhada nas seções a seguir.

CORS com política nomeada e middleware

O CORS Middleware lida com solicitações de origens cruzadas. O seguinte código aplica uma política CORS a todos os endpoints da aplicação com as especificadas origens:

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

O código anterior:

Com o roteamento de endpoint, o middleware CORS deve ser configurado para ser executado entre as chamadas para UseRouting e UseEndpoints.

Consulte Test CORS para obter instruções sobre como testar um código semelhante ao código anterior.

A AddCors chamada de método adiciona serviços CORS ao contêiner de serviço do aplicativo:

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 obter mais informações, consulte Opções de política CORS neste documento.

Os CorsPolicyBuilder métodos podem ser encadeados, como mostrado no código a seguir:

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: O URL especificado não deve conter uma barra final (/). Se a URL terminar com /, a comparação retornará false e nenhum cabeçalho será retornado.

CORS com política padrão e middleware

O código realçado a seguir habilita a política CORS padrão:

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

O código anterior aplica a política CORS padrão a todos os pontos de extremidade do controlador.

Habilitar Cors com roteamento de ponto final

A ativação do CORS por endpoint não RequireCorssuporta solicitações automáticas de preflight. Para obter mais informações, consulte este problema do GitHub e Teste o CORS com roteamento por endpoint e [HttpOptions].

Com o roteamento de pontos finais, o CORS pode ser habilitado por ponto final usando o RequireCors conjunto de métodos de extensão:

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

No código anterior:

  • app.UseCors habilita o middleware CORS. Como uma política padrão não foi configurada, app.UseCors() sozinha não habilita o CORS.
  • Os /echo endpoints e controladores permitem solicitações de origem cruzada usando a política especificada.
  • Os endereços /echo2 e Razor Pages não permitem solicitações de origem cruzada porque nenhuma política padrão foi especificada.

O atributo [DisableCors]não desabilita o CORS que foi habilitado pelo roteamento de ponto de extremidade com RequireCors.

Consulte Testar CORS com roteamento de ponto de extremidade e [HttpOptions] para obter instruções sobre como testar código semelhante ao anterior.

Habilitar CORS com atributos

Habilitar CORS com o atributo [EnableCors] e aplicar uma política nomeada somente aos pontos de extremidade que exigem CORS fornece o melhor controle.

O atributo [EnableCors] fornece uma alternativa à aplicação global de CORS. O [EnableCors] atributo habilita CORS para pontos de extremidade selecionados, em vez de todos os pontos de extremidade:

  • [EnableCors] Especifica a política padrão.
  • [EnableCors("{Policy String}")] Especifica uma política nomeada.

O atributo [EnableCors] pode ser aplicado a:

  • Razor Página PageModel
  • Controller
  • Método de ação do controlador

Políticas diferentes podem ser aplicadas a controladores, modelos de página ou métodos de ação com o [EnableCors] atributo. Quando o atributo é aplicado a um controlador, modelo de página ou método de ação, e o [EnableCors] CORS é habilitado no middleware, ambas as políticas são aplicadas. Recomendamos que não se combine políticas. Utilize a seringa [EnableCors] atributo ou middleware, não ambos no mesmo aplicativo.

O código a seguir aplica uma política diferente para 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(),
        };
    }
}

O código a seguir cria duas 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 o melhor controle da limitação de solicitações CORS:

  • Utilize [EnableCors("MyPolicy")] com uma política nomeada.
  • Não defina uma política padrão.
  • Não use roteamento de ponto final.

O código na próxima seção atende à lista anterior.

Consulte Test CORS para obter instruções sobre como testar um código semelhante ao código anterior.

Desativar CORS

O atributo [DisableCors]não desabilita o CORS que foi habilitado pelo roteamento de ponto de extremidade.

O código a seguir define a política "MyPolicy"CORS :

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

O código a seguir desabilita o CORS para a GetValues2 ação:

[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();

}

O código anterior:

Consulte Test CORS para obter instruções sobre como testar o código anterior.

Opções políticas do CORS

Esta seção descreve as várias opções que podem ser definidas em uma política CORS:

AddPolicy é chamado em Startup.ConfigureServices. Para algumas opções, pode ser útil ler primeiro a seção Como funciona o CORS .

Definir as origens permitidas

AllowAnyOrigin: Permite solicitações CORS de todas as origens com qualquer esquema (http ou https). AllowAnyOrigin é inseguro porque qualquer site pode fazer solicitações de origem cruzada para o aplicativo.

Note

Especificar AllowAnyOrigin e AllowCredentials é uma configuração insegura e pode resultar em falsificação de solicitação entre sites. O serviço CORS retorna uma resposta CORS inválida quando um aplicativo é configurado com ambos os métodos.

AllowAnyOrigin afeta as solicitações de pré-verificação e o cabeçalho Access-Control-Allow-Origin. Para obter mais informações, consulte a seção Solicitações Preflight.

SetIsOriginAllowedToAllowWildcardSubdomains: Define a IsOriginAllowed propriedade da política como uma função que permite que as origens correspondam a um domínio curinga configurado ao avaliar se a origem é permitida.

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

No código anterior, SetIsOriginAllowedToAllowWildcardSubdomains é chamado com a base de origem "https://example.com". Essa configuração permite solicitações CORS de qualquer subdomínio de example.com, como https://subdomain.example.com ou https://api.example.com. A correspondência de caracteres curinga é tratada pelo método, portanto, a origem deve ser especificada sem o caractere curinga * .

Definir os métodos HTTP permitidos

AllowAnyMethod:

  • Permite qualquer método HTTP:
  • Afecta as solicitações de comprovação e o Access-Control-Allow-Methods cabeçalho. Para obter mais informações, consulte a seção Solicitações Preflight.

Definir os cabeçalhos de solicitação permitidos

Para permitir que cabeçalhos específicos sejam enviados em uma solicitação CORS, chamados cabeçalhos de solicitação de autor, chame WithHeaders e especifique os cabeçalhos 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 os cabeçalhos de solicitação do utilizador, chame AllowAnyHeader:

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

AllowAnyHeader afeta as solicitações preliminares e o cabeçalho Access-Control-Request-Headers. Para obter mais informações, consulte a seção Solicitações Preflight.

Uma correspondência de política de middleware CORS com cabeçalhos específicos especificados por WithHeaders só é possível quando os cabeçalhos enviados em Access-Control-Request-Headers correspondam exatamente aos cabeçalhos indicados em WithHeaders.

Por exemplo, considere um aplicativo configurado da seguinte maneira:

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

O CORS Middleware recusa um pedido preliminar com o seguinte cabeçalho de solicitação devido ao fato de que Content-Language (HeaderNames.ContentLanguage) não está listado em WithHeaders:

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

O aplicativo retorna uma resposta 200 OK , mas não envia os cabeçalhos CORS de volta. Portanto, o navegador não tenta a solicitação de origem cruzada.

Definir os cabeçalhos de resposta expostos

Por padrão, o navegador não expõe todos os cabeçalhos de resposta ao aplicativo. Para obter mais informações, consulte W3C Cross-Origin Resource Sharing (Terminology): Simple Response Header.

Os cabeçalhos de resposta disponíveis por padrão são:

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

A especificação CORS chama esses cabeçalhos de cabeçalhos de resposta simples. Para disponibilizar outros cabeçalhos para o aplicativo, chame WithExposedHeaders:

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

Credenciais em solicitações de origem cruzada

As credenciais exigem tratamento especial em uma solicitação CORS. Por padrão, o navegador não envia credenciais com uma solicitação de origem cruzada. As credenciais incluem cookies e esquemas de autenticação HTTP. Para enviar credenciais com uma solicitação de origem cruzada, o cliente deve definir XMLHttpRequest.withCredentials como true.

Usando XMLHttpRequest diretamente:

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

Usando o jQuery:

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

Usando a Fetch API:

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

O servidor deve permitir as credenciais. Para permitir credenciais de origem cruzada, chame AllowCredentials:

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

A resposta HTTP inclui um Access-Control-Allow-Credentials cabeçalho, que informa ao navegador que o servidor permite credenciais para uma solicitação de origem cruzada.

Se o navegador enviar credenciais, mas a resposta não incluir um cabeçalho válido Access-Control-Allow-Credentials , o navegador não exporá a resposta ao aplicativo e a solicitação de origem cruzada falhará.

Permitir credenciais de origem cruzada é um risco de segurança. Um site em outro domínio pode enviar as credenciais de um usuário conectado para o aplicativo em nome do usuário sem o conhecimento do usuário.

A especificação CORS também afirma que definir origens como "*" (todas as origens) é inválido se o Access-Control-Allow-Credentials cabeçalho estiver presente.

Pedidos de comprovação

Para algumas solicitações CORS, o navegador envia uma solicitação OPTIONS adicional antes de fazer a solicitação real. Essa solicitação é chamada de solicitação preliminar. O navegador pode ignorar a solicitação de comprovação se todas as seguintes condições forem verdadeiras:

  • O método de solicitação é GET, HEAD ou POST.
  • O aplicativo não define cabeçalhos de solicitação diferentes de Accept, Accept-Language, Content-Language, Content-Type, ou Last-Event-ID.
  • O Content-Type cabeçalho, se definido, tem um dos seguintes valores:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

A regra relativa aos cabeçalhos de solicitação definidos para a solicitação do cliente aplica-se aos cabeçalhos que o aplicativo define ao chamar setRequestHeader no objeto XMLHttpRequest. A especificação CORS chama esses cabeçalhos de cabeçalhos de solicitação de autor. A regra não se aplica a cabeçalhos que o navegador pode definir, como User-Agent, Hostou Content-Length.

A seguir está um exemplo de resposta semelhante ao pedido de preflight feito a partir do botão [Colocar Teste] na seção Test CORS deste 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

O pedido preflight usa o método HTTP OPTIONS. Pode incluir os seguintes cabeçalhos:

Se a solicitação de pre-verificação for negada, o aplicativo retornará uma 200 OK resposta, mas não definirá os cabeçalhos CORS. Portanto, o navegador não tenta a solicitação de origem cruzada. Para obter um exemplo de um pedido preflight negado, consulte a seção Test CORS deste documento.

Usando as ferramentas F12, o aplicativo de console mostra um erro semelhante a um dos seguintes, dependendo do navegador:

  • Firefox: Cross-Origin Request Blocked: A mesma política de origem não permite a leitura do recurso remoto em https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Motivo: o pedido do CORS não teve sucesso). Saiba mais
  • Baseado no Chromium: O acesso para buscar em 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' da origem 'https://cors3.azurewebsites.net' foi bloqueado pela política CORS: A resposta à solicitação de pré-vôo não passa na verificação de controle de acesso: Nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Se uma resposta opaca atender às suas necessidades, defina o modo da solicitação como 'no-cors' para buscar o recurso com o CORS desativado.

Para permitir cabeçalhos específicos, chame 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 os cabeçalhos de solicitação do utilizador, chame AllowAnyHeader:

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

Os navegadores não são consistentes na maneira como definem Access-Control-Request-Headers. Em caso afirmativo:

  • Os cabeçalhos são definidos para qualquer coisa diferente de "*"
  • AllowAnyHeader é chamado: Inclua pelo menos Accept, Content-Type, e Origin, além de quaisquer cabeçalhos personalizados que deseje suportar.

Código automático de pedido de comprovação

Quando a política CORS é aplicada, quer seja de uma forma ou de outra:

  • Globalmente, ligando para app.UseCors em Startup.Configure.
  • Usando o [EnableCors] atributo.

ASP.NET Core responde à solicitação OPTIONS de pré-verificação.

Habilitar o CORS por endpoint usando RequireCors atualmente não suporta solicitações automáticas de pré-verificação.

A seção Test CORS deste documento demonstra esse comportamento.

Atributo [HttpOptions] para pedidos de pré-verificação

Quando o CORS é ativado com a política apropriada, o ASP.NET Core geralmente responde automaticamente às solicitações preflight de CORS. Em alguns cenários, pode não ser esse o caso. Por exemplo, usando CORS com roteamento de ponto final.

O código a seguir usa o atributo [HttpOptions] para criar pontos de extremidade para solicitações 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 Testar CORS com roteamento de ponto de extremidade e [HttpOptions] para obter instruções sobre como testar o código anterior.

Definir o tempo de expiração da verificação prévia

O Access-Control-Max-Age cabeçalho especifica quanto tempo a resposta para a requisição preflight pode ser armazenada em cache. Para definir esse cabeçalho, chame SetPreflightMaxAge:

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

Como funciona o CORS

Esta seção descreve o que acontece em uma solicitação CORS no nível das mensagens HTTP.

  • O CORS não é um elemento de segurança. O CORS é um padrão W3C que permite que um servidor relaxe a política de mesma origem.
    • Por exemplo, um ator mal-intencionado pode usar Cross-Site Scripting (XSS) no seu site e executar uma solicitação entre sites para um site com CORS ativado, a fim de roubar informações.
  • Uma API não é mais segura ao permitir CORS.
    • Cabe ao cliente (navegador) aplicar o CORS. O servidor executa a solicitação e retorna a resposta, é o cliente que retorna um erro e bloqueia a resposta. Por exemplo, qualquer uma das seguintes ferramentas exibirá a resposta do servidor:
  • É uma maneira de um servidor permitir que os navegadores executem um pedido XHR ou Fetch API entre origens cruzadas que de outra forma seria proibido.
    • Navegadores sem CORS não podem fazer solicitações de origens cruzadas. Antes do CORS, o JSONP era usado para contornar essa restrição. JSONP não usa XHR, ele usa a <script> tag para receber a resposta. Os scripts podem ser carregados entre origens.

A especificação CORS introduziu vários novos cabeçalhos HTTP que permitem solicitações de origens cruzadas. Se um navegador suportar CORS, ele define esses cabeçalhos automaticamente para solicitações de origem cruzada. O código JavaScript personalizado não é necessário para ativar o CORS.

A seguir está um exemplo de uma solicitação de origem cruzada do botão de teste Valores para https://cors1.azurewebsites.net/api/values. O Origin cabeçalho:

  • Fornece o domínio do site que está fazendo a solicitação.
  • É obrigatório e deve ser diferente do servidor.

Cabeçalhos gerais

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

Cabeçalhos de resposta

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

Cabeçalhos de solicitação

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

Nas OPTIONS solicitações, o servidor define o cabeçalho de respostaAccess-Control-Allow-Origin: {allowed origin} na resposta. Por exemplo, a amostra implantada, a solicitação do botão OPTIONS Excluir contém os seguintes cabeçalhos:

Cabeçalhos gerais

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

Cabeçalhos de resposta

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

Cabeçalhos de solicitação

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

Nos cabeçalhos de resposta anteriores, o servidor define o cabeçalho Access-Control-Allow-Origin na resposta. O https://cors1.azurewebsites.net valor desse cabeçalho corresponde ao Origin cabeçalho da solicitação.

Se AllowAnyOrigin for chamado, o Access-Control-Allow-Origin: *, o valor curinga, é retornado. AllowAnyOrigin permite qualquer origem.

Se a resposta não incluir o Access-Control-Allow-Origin cabeçalho, a solicitação de origem cruzada falhará. Especificamente, o navegador não permite a solicitação. Mesmo que o servidor retorne uma resposta bem-sucedida, o navegador não disponibilizará a resposta para o aplicativo cliente.

Exibir solicitações OPÇÕES

Por padrão, os navegadores Chrome e Edge não mostram solicitações OPTIONS no separador de rede, das ferramentas F12. Para exibir solicitações OPTIONS nestes navegadores:

  • chrome://flags/#out-of-blink-cors ou edge://flags/#out-of-blink-cors
  • Desative o sinalizador.
  • restart.

O Firefox mostra solicitações de OPÇÕES por padrão.

CORS no IIS

Ao implantar no IIS, o CORS deve ser executado antes da Autenticação do Windows se o servidor não estiver configurado para permitir acesso anônimo. Para dar suporte a esse cenário, o módulo CORS do IIS precisa ser instalado e configurado para o aplicativo.

Teste CORS

O download de exemplo tem código para testar o CORS. Veja como fazer o download. O exemplo é um projeto de API com páginas Razor adicionadas:

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

Warning

WithOrigins("https://localhost:<port>"); só deve ser usado para testar um aplicativo de exemplo semelhante ao código de exemplo de download.

O seguinte ValuesController fornece os endereços para teste:

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

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

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


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

}

MyDisplayRouteInfo é fornecido pelo pacote Rick.Docs.Samples.RouteInfo NuGet e exibe informações de rota.

Teste o código de exemplo anterior usando uma das seguintes abordagens:

  • Execute o exemplo com dotnet run usando a URL padrão de https://localhost:5001.
  • Execute o exemplo do Visual Studio com a porta definida como 44398 para uma URL de https://localhost:44398.

Usando um navegador com as ferramentas F12:

  • Selecione o botão Valores e revise os cabeçalhos na guia Rede .

  • Selecione o botão de teste PUT . Consulte Exibir solicitações OPTIONS para obter instruções sobre como exibir a solicitação OPTIONS. O teste PUT cria duas solicitações: uma solicitação preflight OPTIONS e uma solicitação PUT.

  • Selecione o GetValues2 [DisableCors] botão para acionar uma solicitação CORS com falha. Como mencionado no documento, a resposta retorna o código 200 de sucesso, mas a requisição CORS não é feita. Selecione a guia Console para ver o erro CORS. Dependendo do navegador, um erro semelhante ao seguinte é exibido:

    O acesso ao recurso em 'https://cors1.azurewebsites.net/api/values/GetValues2' da origem 'https://cors3.azurewebsites.net' foi bloqueado pela política CORS: nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Se uma resposta opaca atender às suas necessidades, defina o modo da solicitação como 'no-cors' para buscar o recurso com o CORS desativado.

Os endpoints habilitados para CORS podem ser testados com uma ferramenta, como curl ou Fiddler. Ao usar uma ferramenta, a origem da solicitação especificada pelo Origin cabeçalho deve ser diferente do host que recebe a solicitação. Se a solicitação não for de origem cruzada com base no valor do Origin cabeçalho:

  • Não há necessidade de o CORS Middleware processar a solicitação.
  • Os cabeçalhos CORS não são retornados na resposta.

O comando a seguir é usado curl para emitir uma solicitação OPTIONS com informações:

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

Teste CORS com roteamento de ponto final e [HttpOptions]

Habilitar o CORS em uma base por endpoint usando RequireCors atualmente não suporta pedidos preflight automáticos. Considere o seguinte código que utiliza o roteamento de endpoints para ativar o 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();
        });
    }
}

O seguinte TodoItems1Controller fornece endpoints para testes:

[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);
}

Teste o código precedente na página de teste (https://cors1.azurewebsites.net/test?number=1) do exemplo implementado.

Os botões Delete [EnableCors] e GET [EnableCors] são bem-sucedidos, porque os endpoints têm [EnableCors] e respondem a pedidos preflight. Os outros pontos finais falham. O botão GET falha, porque o JavaScript envia:

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

O seguinte TodoItems2Controller fornece endpoints semelhantes, mas inclui código explícito para responder a pedidos 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);
}

O código anterior pode ser testado ao implantar o exemplo no Azure. Na lista suspensa Controlador, selecione Pré-voo e, em seguida, Definir controlador. Todas as chamadas CORS para os TodoItems2Controller endpoints são realizadas com sucesso.

Recursos adicionais