Condividi tramite


Abilitare le richieste tra le origini (CORS) in ASP.NET Core

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Avviso

Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Importante

Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.

Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Di Rick Anderson e Kirk Larkin

Questo articolo illustra in che modo Cross-O rigin Resource Sharing (CORS) è abilitato in un'app ASP.NET Core.

La sicurezza del browser impedisce a una pagina Web di effettuare richieste a un dominio diverso da quello che ha distribuito la pagina Web. Questa restrizione è nota come criteri di corrispondenza dell'origine. I criteri di corrispondenza dell'origine impediscono a un sito dannoso di accedere a dati sensibili in un altro sito. In alcuni casi, potrebbe essere necessario consentire ad altri siti di effettuare richieste tra origini all'app. Per altre informazioni, vedere l'articolo Mozilla CORS.

Condivisione di risorse tra le origini (CORS):

  • È uno standard W3C che consente a un server di rilassare i criteri di stessa origine.
  • Non è una funzionalità di sicurezza, CORS riduce la sicurezza. Un'API non è più sicura consentendo CORS. Per altre informazioni, vedere Funzionamento di CORS.
  • Consente a un server di consentire in modo esplicito alcune richieste tra le origini rifiutando altre.
  • È più sicuro e più flessibile rispetto alle tecniche precedenti, ad esempio JSONP.

Visualizzare o scaricare il codice di esempio (procedura per il download)

Stessa origine

Due URL hanno la stessa origine se hanno schemi, host e porte identici (RFC 6454).

Questi due URL hanno la stessa origine:

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

Questi URL hanno origini diverse rispetto ai due URL precedenti:

  • https://example.net: dominio diverso
  • https://contoso.example.com/foo.html: sottodominio diverso
  • http://example.com/foo.html: schema diverso
  • https://example.com:9000/foo.html: porta diversa

Abilitare CORS

Esistono tre modi per abilitare CORS:

L'uso dell'attributo [EnableCors] con un criterio denominato consente di controllare meglio gli endpoint che supportano CORS.

Avviso

UseCors deve essere chiamato nell'ordine corretto. Per altre informazioni, vedere Ordine del middleware. Ad esempio, UseCors deve essere chiamato prima UseResponseCaching di quando si usa UseResponseCaching.

Ogni approccio è dettagliato nelle sezioni seguenti.

CORS con criteri e middleware denominati

Il middleware CORS gestisce le richieste tra le origini. Il codice seguente applica un criterio CORS a tutti gli endpoint dell'app con le origini specificate:

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

Il codice precedente:

  • Imposta il nome del criterio su _myAllowSpecificOrigins. Il nome del criterio è arbitrario.
  • Chiama il UseCors metodo di estensione e specifica i _myAllowSpecificOrigins criteri CORS. UseCors aggiunge il middleware CORS. La chiamata a UseCors deve essere inserita dopo UseRouting, ma prima UseAuthorizationdi . Per altre informazioni, vedere Ordine del middleware.
  • Chiamate AddCors con un'espressione lambda. L'espressione lambda accetta un CorsPolicyBuilder oggetto . Le opzioni di configurazione, ad esempio WithOrigins, sono descritte più avanti in questo articolo.
  • Abilita i _myAllowSpecificOrigins criteri CORS per tutti gli endpoint controller. Vedere Routing degli endpoint per applicare un criterio CORS a endpoint specifici.
  • Quando si usa il middleware di memorizzazione nella cache delle risposte, chiamare UseCors prima UseResponseCachingdi .

Con il routing degli endpoint, il middleware CORS deve essere configurato per l'esecuzione tra le chiamate a UseRouting e UseEndpoints.

La AddCors chiamata al metodo aggiunge servizi CORS al contenitore del servizio dell'app:

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

Per altre informazioni, vedere Opzioni dei criteri CORS in questo documento.

I CorsPolicyBuilder metodi possono essere concatenati, come illustrato nel codice seguente:

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: l'URL specificato non deve contenere una barra finale (/). Se l'URL termina con /, il confronto restituisce false e non viene restituita alcuna intestazione.

Ordine UseCors e UseStaticFiles

In genere, UseStaticFiles viene chiamato prima UseCorsdi . Le app che usano JavaScript per recuperare i file statici tra siti devono chiamare UseCors prima UseStaticFilesdi .

CORS con criteri e middleware predefiniti

Il codice evidenziato seguente abilita i criteri CORS predefiniti:

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

Il codice precedente applica i criteri CORS predefiniti a tutti gli endpoint controller.

Abilitare Cors con il routing degli endpoint

Con il routing degli endpoint, CORS può essere abilitato per ogni endpoint usando il RequireCors set di metodi di estensione:

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

Nel codice precedente:

  • app.UseCors abilita il middleware CORS. Poiché non è stato configurato un criterio predefinito, app.UseCors() da solo non abilita CORS.
  • Gli /echo endpoint del controller e consentono le richieste tra le origini usando i criteri specificati.
  • Gli /echo2 endpoint e Razor Pages non consentono le richieste tra le origini perché non è stato specificato alcun criterio predefinito.

L'attributo [DisableCors] non disabilita CORS abilitato dal routing degli endpoint con RequireCors.

Vedere Testare CORS con l'attributo [EnableCors] e il metodo RequireCors per istruzioni sul test del codice simile al precedente.

Abilitare CORS con attributi

L'abilitazione di CORS con l'attributo [EnableCors] e l'applicazione di un criterio denominato solo agli endpoint che richiedono CORS fornisce il controllo migliore.

L'attributo [EnableCors] offre un'alternativa all'applicazione globale di CORS. L'attributo [EnableCors] abilita CORS per gli endpoint selezionati, anziché per tutti gli endpoint:

  • [EnableCors] specifica il criterio predefinito.
  • [EnableCors("{Policy String}")] specifica un criterio denominato.

L'attributo [EnableCors] può essere applicato a:

  • Razor Pagina PageModel
  • Controller
  • Metodo di azione del controller

È possibile applicare criteri diversi ai controller, ai modelli di pagina o ai metodi di azione con l'attributo [EnableCors] . Quando l'attributo [EnableCors] viene applicato a un controller, a un modello di pagina o a un metodo di azione e CORS è abilitato nel middleware, vengono applicati entrambi i criteri. È consigliabile combinare i criteri. Usare[EnableCors] attributo o middleware, non entrambi nella stessa app.

Il codice seguente applica criteri diversi a ogni metodo:

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

Il codice seguente crea due criteri 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();

Per il controllo più dettagliato della limitazione delle richieste CORS:

  • Usare [EnableCors("MyPolicy")] con un criterio denominato.
  • Non definire un criterio predefinito.
  • Non usare il routing degli endpoint.

Il codice nella sezione successiva soddisfa l'elenco precedente.

Disabilitare CORS

L'attributo [DisableCors] non disabilita CORS abilitato dal routing degli endpoint.

Il codice seguente definisce i criteri CORS "MyPolicy":

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

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

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

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

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

app.Run();

Il codice seguente disabilita CORS per l'azione GetValues2 :

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

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

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


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

}

Il codice precedente:

Vedere Testare CORS per istruzioni sul test del codice precedente.

Opzioni dei criteri CORS

Questa sezione descrive le varie opzioni che è possibile impostare in un criterio CORS:

AddPolicy viene chiamato in Program.cs. Per alcune opzioni, può essere utile leggere prima la sezione Funzionamento di CORS .

Impostare le origini consentite

AllowAnyOrigin: consente le richieste CORS da tutte le origini con qualsiasi schema (http o https). AllowAnyOrigin non è sicuro perché qualsiasi sito Web può effettuare richieste tra le origini all'app.

Nota

Specificare AllowAnyOrigin e AllowCredentials è una configurazione non sicura e può comportare la falsificazione della richiesta intersito. Il servizio CORS restituisce una risposta CORS non valida quando un'app è configurata con entrambi i metodi.

AllowAnyOrigin influisce sulle richieste preliminari e sull'intestazione Access-Control-Allow-Origin . Per altre informazioni, vedere la sezione Richieste preliminari .

SetIsOriginAllowedToAllowWildcardSubdomains: imposta la IsOriginAllowed proprietà del criterio come funzione che consente alle origini di trovare una corrispondenza con un dominio con caratteri jolly configurato durante la valutazione se l'origine è consentita.

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

Impostare i metodi HTTP consentiti

AllowAnyMethod:

  • Consente qualsiasi metodo HTTP:
  • Influisce sulle richieste preliminari e sull'intestazione Access-Control-Allow-Methods . Per altre informazioni, vedere la sezione Richieste preliminari .

Impostare le intestazioni di richiesta consentite

Per consentire l'invio di intestazioni specifiche in una richiesta CORS, denominata intestazioni di richiesta autore, chiamare WithHeaders e specificare le intestazioni consentite:

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

Per consentire tutte le intestazioni di richiesta dell'autore, chiamare 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 influisce sulle richieste preliminari e sull'intestazione Access-Control-Request-Headers . Per altre informazioni, vedere la sezione Richieste preliminari .

Un criterio middleware CORS corrisponde a intestazioni specifiche specificate da WithHeaders è possibile solo quando le intestazioni inviate in Access-Control-Request-Headers corrispondono esattamente alle intestazioni indicate in WithHeaders.

Si consideri, ad esempio, un'app configurata come segue:

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

Il middleware CORS rifiuta una richiesta preliminare con l'intestazione di richiesta seguente perché Content-Language (HeaderNames.ContentLanguage) non è elencato in WithHeaders:

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

L'app restituisce una risposta 200 OK , ma non invia di nuovo le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra le origini.

Impostare le intestazioni di risposta esposte

Per impostazione predefinita, il browser non espone tutte le intestazioni di risposta all'app. Per altre informazioni, vedere W3C Cross-Origin Resource Sharing (Terminologia): Intestazione di risposta semplice.

Le intestazioni di risposta disponibili per impostazione predefinita sono:

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

La specifica CORS chiama queste intestazioni semplici intestazioni di risposta. Per rendere disponibili altre intestazioni all'app, chiamare 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();

Credenziali nelle richieste tra le origini

Le credenziali richiedono una gestione speciale in una richiesta CORS. Per impostazione predefinita, il browser non invia le credenziali con una richiesta tra le origini. Le credenziali includono cookie e schemi di autenticazione HTTP. Per inviare le credenziali con una richiesta tra le origini, il client deve impostare su XMLHttpRequest.withCredentials true.

Uso XMLHttpRequest diretto:

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

Uso di jQuery:

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

Uso dell'API Fetch:

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

Il server deve consentire le credenziali. Per consentire le credenziali tra le origini, chiamare AllowCredentials:

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddControllers();

var app = builder.Build();

La risposta HTTP include un'intestazione Access-Control-Allow-Credentials che indica al browser che il server consente le credenziali per una richiesta tra le origini.

Se il browser invia credenziali ma la risposta non include un'intestazione valida Access-Control-Allow-Credentials , il browser non espone la risposta all'app e la richiesta tra le origini ha esito negativo.

Consentire le credenziali tra le origini è un rischio per la sicurezza. Un sito Web in un altro dominio può inviare le credenziali di un utente connesso all'app per conto dell'utente senza conoscere l'utente.

La specifica CORS indica inoltre che l'impostazione delle origini su "*" (tutte le origini) non è valida se l'intestazione Access-Control-Allow-Credentials è presente.

Richieste preliminari

Per alcune richieste CORS, il browser invia una richiesta OPTIONS aggiuntiva prima di effettuare la richiesta effettiva. Questa richiesta viene chiamata richiesta preliminare. Il browser può ignorare la richiesta preliminare se sono soddisfatte tutte le condizioni seguenti:

  • Il metodo di richiesta è GET, HEAD o POST.
  • L'app non imposta intestazioni di richiesta diverse da Accept, Accept-Language, Content-LanguageContent-Type, o Last-Event-ID.
  • L'intestazione Content-Type , se impostata, ha uno dei valori seguenti:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

La regola sulle intestazioni di richiesta impostata per la richiesta client si applica alle intestazioni impostate dall'app chiamando setRequestHeader sull'oggetto XMLHttpRequest . La specifica CORS chiama queste intestazioni le intestazioni di richiesta dell'autore. La regola non si applica alle intestazioni che il browser può impostare, ad esempio User-Agent, Hosto Content-Length.

Nota

Questo articolo contiene gli URL creati distribuendo il codice di esempio in due siti Web di Azure e https://cors3.azurewebsites.net https://cors.azurewebsites.net.

Di seguito è riportata una risposta di esempio simile alla richiesta preliminare effettuata dal pulsante [Put test] nella sezione Test CORS di questo documento.

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

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

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

La richiesta preliminare usa il metodo HTTP OPTIONS . Può includere le intestazioni seguenti:

  • Access-Control-Request-Method: metodo HTTP che verrà usato per la richiesta effettiva.
  • Access-Control-Request-Headers: elenco di intestazioni di richiesta impostate dall'app nella richiesta effettiva. Come indicato in precedenza, non include intestazioni impostate dal browser, ad esempio User-Agent.

Se la richiesta preliminare viene negata, l'app restituisce una 200 OK risposta ma non imposta le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra le origini. Per un esempio di richiesta preliminare negata, vedere la sezione Test CORS di questo documento.

Usando gli strumenti F12, l'app console mostra un errore simile a uno dei seguenti, a seconda del browser:

  • Firefox: Richiesta tra le origini bloccate: lo stesso criterio di origine non consente la lettura della risorsa remota in https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Motivo: richiesta CORS non riuscita). Altre informazioni
  • Basato su Chromium: l'accesso al recupero all'origine 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' dall'origine 'https://cors3.azurewebsites.net' è stato bloccato dai criteri CORS: la risposta alla richiesta preliminare non supera il controllo di accesso: nessuna intestazione 'Access-Control-Allow-Origin' è presente nella risorsa richiesta. Se si ritiene soddisfacente una risposta opaca, impostare la modalità della richiesta 'no-cors' per recuperare la risorsa con CORS disabilitato.

Per consentire intestazioni specifiche, chiamare 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();

Per consentire tutte le intestazioni di richiesta dell'autore, chiamare 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();

I browser non sono coerenti nel modo in cui impostano Access-Control-Request-Headers. In caso affermativo:

  • Le intestazioni sono impostate su un valore diverso da "*"
  • AllowAnyHeader viene chiamato: includere almeno Accept, Content-Typee Origin, più eventuali intestazioni personalizzate che si desidera supportare.

Codice di richiesta preliminare automatico

Quando vengono applicati i criteri CORS:

  • A livello globale chiamando app.UseCors in Program.cs.
  • Uso dell'attributo [EnableCors] .

ASP.NET Core risponde alla richiesta OPTIONS preliminare.

La sezione Test CORS di questo documento illustra questo comportamento.

Attributo [HttpOptions] per le richieste preliminari

Quando CORS è abilitato con i criteri appropriati, ASP.NET Core risponde in genere alle richieste preliminari CORS automaticamente.

Il codice seguente usa l'attributo [HttpOptions] per creare endpoint per le richieste 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);
    }

Vedere Testare CORS con l'attributo [EnableCors] e il metodo RequireCors per istruzioni sul test del codice precedente.

Impostare l'ora di scadenza preliminare

L'intestazione Access-Control-Max-Age specifica per quanto tempo è possibile memorizzare nella cache la risposta alla richiesta preliminare. Per impostare questa intestazione, chiamare 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();

Abilitare CORS in un endpoint

Funzionamento di CORS

Questa sezione descrive cosa accade in una richiesta CORS a livello di messaggi HTTP.

  • CORS non è una funzionalità di sicurezza. CORS è uno standard W3C che consente a un server di ridurre i criteri di stessa origine.
    • Ad esempio, un attore malintenzionato potrebbe usare xss (Cross-Site Scripting) sul sito ed eseguire una richiesta tra siti al sito abilitato per CORS per rubare informazioni.
  • Un'API non è più sicura consentendo CORS.
    • Spetta al client (browser) applicare CORS. Il server esegue la richiesta e restituisce la risposta, è il client che restituisce un errore e blocca la risposta. Ad esempio, uno degli strumenti seguenti visualizzerà la risposta del server:
  • È un modo per consentire ai browser di eseguire una richiesta XHR o Fetch API di origine incrociata che altrimenti sarebbe vietata.
    • I browser senza CORS non possono eseguire richieste tra le origini. Prima di CORS, JSONP veniva usato per aggirare questa restrizione. JSONP non usa XHR, ma usa il <script> tag per ricevere la risposta. Gli script possono essere caricati tra le origini.

La specifica CORS ha introdotto diverse nuove intestazioni HTTP che abilitano le richieste tra le origini. Se un browser supporta CORS, imposta automaticamente queste intestazioni per le richieste tra le origini. Il codice JavaScript personalizzato non è necessario per abilitare CORS.

Di seguito è riportato un esempio di richiesta tra le origini dal pulsante di test Values a https://cors1.azurewebsites.net/api/values. Intestazione Origin :

  • Fornisce il dominio del sito che effettua la richiesta.
  • È obbligatorio e deve essere diverso dall'host.

Intestazioni generali

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

Intestazioni di risposta

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

Intestazioni delle richieste

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

Nelle OPTIONS richieste, il server imposta l'intestazione Intestazioni Access-Control-Allow-Origin: {allowed origin} risposta nella risposta. Nel codice di esempio, ad esempio, la Delete [EnableCors] richiesta del pulsante OPTIONS contiene le intestazioni seguenti:

Intestazioni generali

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

Intestazioni di risposta

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

Intestazioni delle richieste

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

Nelle intestazioni response precedenti il server imposta l'intestazione Access-Control-Allow-Origin nella risposta. Il https://cors1.azurewebsites.net valore di questa intestazione corrisponde all'intestazione Origin della richiesta.

Se AllowAnyOrigin viene chiamato , Access-Control-Allow-Origin: *viene restituito il valore con caratteri jolly . AllowAnyOrigin consente qualsiasi origine.

Se la risposta non include l'intestazione Access-Control-Allow-Origin , la richiesta tra le origini ha esito negativo. In particolare, il browser non consente la richiesta. Anche se il server restituisce una risposta corretta, il browser non rende disponibile la risposta all'app client.

Il reindirizzamento HTTP a HTTPS causa ERR_INVALID_REDIRECT nella richiesta preliminare CORS

Le richieste a un endpoint che usano HTTP reindirizzate a HTTPS UseHttpsRedirection hanno esito negativo con ERR_INVALID_REDIRECT on the CORS preflight request.

I progetti API possono rifiutare le richieste HTTP anziché usarle UseHttpsRedirection per reindirizzare le richieste a HTTPS.

CORS in IIS

Quando si esegue la distribuzione in IIS, CORS deve essere eseguito prima dell'autenticazione di Windows se il server non è configurato per consentire l'accesso anonimo. Per supportare questo scenario, è necessario installare e configurare il modulo CORS IIS per l'app.

Testare CORS

Il download di esempio include codice per testare CORS. Vedere come scaricare un esempio. L'esempio è un progetto API con Razor Pages aggiunto:

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

Avviso

WithOrigins("https://localhost:<port>");deve essere usato solo per testare un'app di esempio simile al codice di esempio di download.

Di seguito ValuesController vengono forniti gli endpoint per il test:

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

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

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


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

}

MyDisplayRouteInfo viene fornito dal pacchetto NuGet Rick.Docs.Samples.RouteInfo e visualizza le informazioni sulla route.

Testare il codice di esempio precedente usando uno degli approcci seguenti:

  • Eseguire l'esempio con dotnet run usando l'URL predefinito di https://localhost:5001.
  • Eseguire l'esempio da Visual Studio con la porta impostata su 44398 per un URL di https://localhost:44398.

Uso di un browser con gli strumenti F12:

  • Selezionare il pulsante Valori ed esaminare le intestazioni nella scheda Rete .

  • Selezionare il pulsante PUT test.Select the PUT test button. Per istruzioni sulla visualizzazione della richiesta OPTIONS, vedere Visualizzare le richieste OPTIONS. Il test PUT crea due richieste, una richiesta preliminare OPTIONS e la richiesta PUT.

  • Selezionare il GetValues2 [DisableCors] pulsante per attivare una richiesta CORS non riuscita. Come accennato nel documento, la risposta restituisce 200 risultati positivi, ma la richiesta CORS non viene effettuata. Selezionare la scheda Console per visualizzare l'errore CORS. A seconda del browser, viene visualizzato un errore simile al seguente:

    L'accesso al recupero dall'origine 'https://cors1.azurewebsites.net/api/values/GetValues2' 'https://cors3.azurewebsites.net' è stato bloccato dai criteri CORS: nessuna intestazione "Access-Control-Allow-Origin" è presente nella risorsa richiesta. Se si ritiene soddisfacente una risposta opaca, impostare la modalità della richiesta 'no-cors' per recuperare la risorsa con CORS disabilitato.

Gli endpoint abilitati per CORS possono essere testati con uno strumento, ad esempio curl o Fiddler. Quando si usa uno strumento, l'origine della richiesta specificata dall'intestazione Origin deve essere diversa dall'host che riceve la richiesta. Se la richiesta non è tra le origini in base al valore dell'intestazione Origin :

  • Non è necessario che il middleware CORS elabori la richiesta.
  • Le intestazioni CORS non vengono restituite nella risposta.

Il comando seguente usa curl per emettere una richiesta OPTIONS con informazioni:

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

Testare CORS con l'attributo [EnableCors] e il metodo RequireCors

Si consideri il codice seguente che usa il routing degli endpoint per abilitare CORS per ogni endpoint 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();

Si noti che solo l'endpoint /echo usa per RequireCors consentire le richieste tra le origini usando i criteri specificati. I controller seguenti abilitano CORS usando l'attributo [EnableCors].

Di seguito TodoItems1Controller sono riportati gli endpoint per il test:

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

        return ControllerContext.MyDisplayRouteInfo(id);
    }

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

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

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

I pulsanti Delete [EnableCors] e GET [EnableCors] hanno esito positivo, perché gli endpoint hanno [EnableCors] e rispondono alle richieste preliminari. Gli altri endpoint hanno esito negativo. Il pulsante GET ha esito negativo perché JavaScript invia:

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

Di seguito TodoItems2Controller sono disponibili endpoint simili, ma include codice esplicito per rispondere alle richieste 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);
}

Il codice precedente può essere testato distribuendo l'esempio in Azure. Nell'elenco a discesa Controller selezionare Preflight (Preflight ) e quindi Set Controller (Imposta controller). Tutte le chiamate CORS agli TodoItems2Controller endpoint hanno esito positivo.

Risorse aggiuntive

Di Rick Anderson e Kirk Larkin

Questo articolo illustra come abilitare CORS in un'app core ASP.NET.

La sicurezza del browser impedisce a una pagina Web di effettuare richieste a un dominio diverso da quello che ha distribuito la pagina Web. Questa restrizione è nota come criteri di corrispondenza dell'origine. I criteri di corrispondenza dell'origine impediscono a un sito dannoso di accedere a dati sensibili in un altro sito. In alcuni casi, potrebbe essere necessario consentire ad altri siti di effettuare richieste tra origini all'app. Per altre informazioni, vedere l'articolo Mozilla CORS.

Condivisione di risorse tra le origini (CORS):

  • È uno standard W3C che consente a un server di rilassare i criteri di stessa origine.
  • Non è una funzionalità di sicurezza, CORS riduce la sicurezza. Un'API non è più sicura consentendo CORS. Per altre informazioni, vedere Funzionamento di CORS.
  • Consente a un server di consentire in modo esplicito alcune richieste tra le origini rifiutando altre.
  • È più sicuro e più flessibile rispetto alle tecniche precedenti, ad esempio JSONP.

Visualizzare o scaricare il codice di esempio (procedura per il download)

Stessa origine

Due URL hanno la stessa origine se hanno schemi, host e porte identici (RFC 6454).

Questi due URL hanno la stessa origine:

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

Questi URL hanno origini diverse rispetto ai due URL precedenti:

  • https://example.net: dominio diverso
  • https://www.example.com/foo.html: sottodominio diverso
  • http://example.com/foo.html: schema diverso
  • https://example.com:9000/foo.html: porta diversa

Abilitare CORS

Esistono tre modi per abilitare CORS:

L'uso dell'attributo [EnableCors] con un criterio denominato consente di controllare meglio gli endpoint che supportano CORS.

Avviso

UseCors deve essere chiamato nell'ordine corretto. Per altre informazioni, vedere Ordine del middleware. Ad esempio, UseCors deve essere chiamato prima UseResponseCaching di quando si usa UseResponseCaching.

Ogni approccio è dettagliato nelle sezioni seguenti.

CORS con criteri e middleware denominati

Il middleware CORS gestisce le richieste tra le origini. Il codice seguente applica un criterio CORS a tutti gli endpoint dell'app con le origini specificate:

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

Il codice precedente:

  • Imposta il nome del criterio su _myAllowSpecificOrigins. Il nome del criterio è arbitrario.
  • Chiama il UseCors metodo di estensione e specifica i _myAllowSpecificOrigins criteri CORS. UseCors aggiunge il middleware CORS. La chiamata a UseCors deve essere inserita dopo UseRouting, ma prima UseAuthorizationdi . Per altre informazioni, vedere Ordine del middleware.
  • Chiamate AddCors con un'espressione lambda. L'espressione lambda accetta un CorsPolicyBuilder oggetto . Le opzioni di configurazione, ad esempio WithOrigins, sono descritte più avanti in questo articolo.
  • Abilita i _myAllowSpecificOrigins criteri CORS per tutti gli endpoint controller. Vedere Routing degli endpoint per applicare un criterio CORS a endpoint specifici.
  • Quando si usa il middleware di memorizzazione nella cache delle risposte, chiamare UseCors prima UseResponseCachingdi .

Con il routing degli endpoint, il middleware CORS deve essere configurato per l'esecuzione tra le chiamate a UseRouting e UseEndpoints.

La AddCors chiamata al metodo aggiunge servizi CORS al contenitore del servizio dell'app:

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

Per altre informazioni, vedere Opzioni dei criteri CORS in questo documento.

I CorsPolicyBuilder metodi possono essere concatenati, come illustrato nel codice seguente:

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: l'URL specificato non deve contenere una barra finale (/). Se l'URL termina con /, il confronto restituisce false e non viene restituita alcuna intestazione.

Avviso

UseCors deve essere posizionato dopo UseRouting e prima UseAuthorizationdi . Ciò consente di assicurarsi che le intestazioni CORS siano incluse nella risposta sia per le chiamate autorizzate che non autorizzate.

Ordine UseCors e UseStaticFiles

In genere, UseStaticFiles viene chiamato prima UseCorsdi . Le app che usano JavaScript per recuperare i file statici tra siti devono chiamare UseCors prima UseStaticFilesdi .

CORS con criteri e middleware predefiniti

Il codice evidenziato seguente abilita i criteri CORS predefiniti:

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

Il codice precedente applica i criteri CORS predefiniti a tutti gli endpoint controller.

Abilitare Cors con il routing degli endpoint

Con il routing degli endpoint, CORS può essere abilitato per ogni endpoint usando il RequireCors set di metodi di estensione:

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

Nel codice precedente:

  • app.UseCors abilita il middleware CORS. Poiché non è stato configurato un criterio predefinito, app.UseCors() da solo non abilita CORS.
  • Gli /echo endpoint del controller e consentono le richieste tra le origini usando i criteri specificati.
  • Gli /echo2 endpoint e Razor Pages non consentono le richieste tra le origini perché non è stato specificato alcun criterio predefinito.

L'attributo [DisableCors] non disabilita CORS abilitato dal routing degli endpoint con RequireCors.

In ASP.NET Core 7.0, l'attributo [EnableCors] deve passare un parametro o un avviso ASP0023 viene generato da una corrispondenza ambigua nella route. ASP.NET Core 8.0 e versioni successive non genera l'avviso ASP0023 .

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

Vedere Testare CORS con l'attributo [EnableCors] e il metodo RequireCors per istruzioni sul test del codice simile al precedente.

Abilitare CORS con attributi

L'abilitazione di CORS con l'attributo [EnableCors] e l'applicazione di un criterio denominato solo agli endpoint che richiedono CORS fornisce il controllo migliore.

L'attributo [EnableCors] offre un'alternativa all'applicazione globale di CORS. L'attributo [EnableCors] abilita CORS per gli endpoint selezionati, anziché per tutti gli endpoint:

  • [EnableCors] specifica il criterio predefinito.
  • [EnableCors("{Policy String}")] specifica un criterio denominato.

L'attributo [EnableCors] può essere applicato a:

  • Razor Pagina PageModel
  • Controller
  • Metodo di azione del controller

È possibile applicare criteri diversi ai controller, ai modelli di pagina o ai metodi di azione con l'attributo [EnableCors] . Quando l'attributo [EnableCors] viene applicato a un controller, a un modello di pagina o a un metodo di azione e CORS è abilitato nel middleware, vengono applicati entrambi i criteri. È consigliabile combinare i criteri. Usare[EnableCors] attributo o middleware, non entrambi nella stessa app.

Il codice seguente applica criteri diversi a ogni metodo:

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

Il codice seguente crea due criteri 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();

Per il controllo più dettagliato della limitazione delle richieste CORS:

  • Usare [EnableCors("MyPolicy")] con un criterio denominato.
  • Non definire un criterio predefinito.
  • Non usare il routing degli endpoint.

Il codice nella sezione successiva soddisfa l'elenco precedente.

Disabilitare CORS

L'attributo [DisableCors] non disabilita CORS abilitato dal routing degli endpoint.

Il codice seguente definisce i criteri CORS "MyPolicy":

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

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

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

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

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

app.Run();

Il codice seguente disabilita CORS per l'azione GetValues2 :

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

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

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


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

}

Il codice precedente:

Vedere Testare CORS per istruzioni sul test del codice precedente.

Opzioni dei criteri CORS

Questa sezione descrive le varie opzioni che è possibile impostare in un criterio CORS:

AddPolicy viene chiamato in Program.cs. Per alcune opzioni, può essere utile leggere prima la sezione Funzionamento di CORS .

Impostare le origini consentite

AllowAnyOrigin: consente le richieste CORS da tutte le origini con qualsiasi schema (http o https). AllowAnyOrigin non è sicuro perché qualsiasi sito Web può effettuare richieste tra le origini all'app.

Nota

Specificare AllowAnyOrigin e AllowCredentials è una configurazione non sicura e può comportare la falsificazione della richiesta intersito. Il servizio CORS restituisce una risposta CORS non valida quando un'app è configurata con entrambi i metodi.

AllowAnyOrigin influisce sulle richieste preliminari e sull'intestazione Access-Control-Allow-Origin . Per altre informazioni, vedere la sezione Richieste preliminari .

SetIsOriginAllowedToAllowWildcardSubdomains: imposta la IsOriginAllowed proprietà del criterio come funzione che consente alle origini di trovare una corrispondenza con un dominio con caratteri jolly configurato durante la valutazione se l'origine è consentita.

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

Impostare i metodi HTTP consentiti

AllowAnyMethod:

  • Consente qualsiasi metodo HTTP:
  • Influisce sulle richieste preliminari e sull'intestazione Access-Control-Allow-Methods . Per altre informazioni, vedere la sezione Richieste preliminari .

Impostare le intestazioni di richiesta consentite

Per consentire l'invio di intestazioni specifiche in una richiesta CORS, denominata intestazioni di richiesta autore, chiamare WithHeaders e specificare le intestazioni consentite:

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

Per consentire tutte le intestazioni di richiesta dell'autore, chiamare 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 influisce sulle richieste preliminari e sull'intestazione Access-Control-Request-Headers . Per altre informazioni, vedere la sezione Richieste preliminari .

Un criterio middleware CORS corrisponde a intestazioni specifiche specificate da WithHeaders è possibile solo quando le intestazioni inviate in Access-Control-Request-Headers corrispondono esattamente alle intestazioni indicate in WithHeaders.

Si consideri, ad esempio, un'app configurata come segue:

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

Il middleware CORS rifiuta una richiesta preliminare con l'intestazione di richiesta seguente perché Content-Language (HeaderNames.ContentLanguage) non è elencato in WithHeaders:

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

L'app restituisce una risposta 200 OK , ma non invia di nuovo le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra le origini.

Impostare le intestazioni di risposta esposte

Per impostazione predefinita, il browser non espone tutte le intestazioni di risposta all'app. Per altre informazioni, vedere W3C Cross-Origin Resource Sharing (Terminologia): Intestazione di risposta semplice.

Le intestazioni di risposta disponibili per impostazione predefinita sono:

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

La specifica CORS chiama queste intestazioni semplici intestazioni di risposta. Per rendere disponibili altre intestazioni all'app, chiamare 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();

Credenziali nelle richieste tra le origini

Le credenziali richiedono una gestione speciale in una richiesta CORS. Per impostazione predefinita, il browser non invia le credenziali con una richiesta tra le origini. Le credenziali includono cookie e schemi di autenticazione HTTP. Per inviare le credenziali con una richiesta tra le origini, il client deve impostare su XMLHttpRequest.withCredentials true.

Uso XMLHttpRequest diretto:

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

Uso di jQuery:

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

Uso dell'API Fetch:

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

Il server deve consentire le credenziali. Per consentire le credenziali tra le origini, chiamare AllowCredentials:

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddControllers();

var app = builder.Build();

La risposta HTTP include un'intestazione Access-Control-Allow-Credentials che indica al browser che il server consente le credenziali per una richiesta tra le origini.

Se il browser invia credenziali ma la risposta non include un'intestazione valida Access-Control-Allow-Credentials , il browser non espone la risposta all'app e la richiesta tra le origini ha esito negativo.

Consentire le credenziali tra le origini è un rischio per la sicurezza. Un sito Web in un altro dominio può inviare le credenziali di un utente connesso all'app per conto dell'utente senza conoscere l'utente.

La specifica CORS indica inoltre che l'impostazione delle origini su "*" (tutte le origini) non è valida se l'intestazione Access-Control-Allow-Credentials è presente.

Richieste preliminari

Per alcune richieste CORS, il browser invia una richiesta OPTIONS aggiuntiva prima di effettuare la richiesta effettiva. Questa richiesta viene chiamata richiesta preliminare. Il browser può ignorare la richiesta preliminare se sono soddisfatte tutte le condizioni seguenti:

  • Il metodo di richiesta è GET, HEAD o POST.
  • L'app non imposta intestazioni di richiesta diverse da Accept, Accept-Language, Content-LanguageContent-Type, o Last-Event-ID.
  • L'intestazione Content-Type , se impostata, ha uno dei valori seguenti:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

La regola sulle intestazioni di richiesta impostata per la richiesta client si applica alle intestazioni impostate dall'app chiamando setRequestHeader sull'oggetto XMLHttpRequest . La specifica CORS chiama queste intestazioni le intestazioni di richiesta dell'autore. La regola non si applica alle intestazioni che il browser può impostare, ad esempio User-Agent, Hosto Content-Length.

Di seguito è riportata una risposta di esempio simile alla richiesta preliminare effettuata dal pulsante [Put test] nella sezione Test CORS di questo documento.

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

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

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

La richiesta preliminare usa il metodo HTTP OPTIONS . Può includere le intestazioni seguenti:

Se la richiesta preliminare viene negata, l'app restituisce una 200 OK risposta ma non imposta le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra le origini. Per un esempio di richiesta preliminare negata, vedere la sezione Test CORS di questo documento.

Usando gli strumenti F12, l'app console mostra un errore simile a uno dei seguenti, a seconda del browser:

  • Firefox: Richiesta tra le origini bloccate: lo stesso criterio di origine non consente la lettura della risorsa remota in https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Motivo: richiesta CORS non riuscita). Altre informazioni
  • Basato su Chromium: l'accesso al recupero all'origine 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' dall'origine 'https://cors3.azurewebsites.net' è stato bloccato dai criteri CORS: la risposta alla richiesta preliminare non supera il controllo di accesso: nessuna intestazione 'Access-Control-Allow-Origin' è presente nella risorsa richiesta. Se si ritiene soddisfacente una risposta opaca, impostare la modalità della richiesta 'no-cors' per recuperare la risorsa con CORS disabilitato.

Per consentire intestazioni specifiche, chiamare 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();

Per consentire tutte le intestazioni di richiesta dell'autore, chiamare 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();

I browser non sono coerenti nel modo in cui impostano Access-Control-Request-Headers. In caso affermativo:

  • Le intestazioni sono impostate su un valore diverso da "*"
  • AllowAnyHeader viene chiamato: includere almeno Accept, Content-Typee Origin, più eventuali intestazioni personalizzate che si desidera supportare.

Codice di richiesta preliminare automatico

Quando vengono applicati i criteri CORS:

  • A livello globale chiamando app.UseCors in Program.cs.
  • Uso dell'attributo [EnableCors] .

ASP.NET Core risponde alla richiesta OPTIONS preliminare.

La sezione Test CORS di questo documento illustra questo comportamento.

Attributo [HttpOptions] per le richieste preliminari

Quando CORS è abilitato con i criteri appropriati, ASP.NET Core risponde in genere alle richieste preliminari CORS automaticamente.

Il codice seguente usa l'attributo [HttpOptions] per creare endpoint per le richieste 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);
    }

Vedere Testare CORS con l'attributo [EnableCors] e il metodo RequireCors per istruzioni sul test del codice precedente.

Impostare l'ora di scadenza preliminare

L'intestazione Access-Control-Max-Age specifica per quanto tempo è possibile memorizzare nella cache la risposta alla richiesta preliminare. Per impostare questa intestazione, chiamare 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();

Abilitare CORS in un endpoint

Funzionamento di CORS

Questa sezione descrive cosa accade in una richiesta CORS a livello di messaggi HTTP.

  • CORS non è una funzionalità di sicurezza. CORS è uno standard W3C che consente a un server di ridurre i criteri di stessa origine.
    • Ad esempio, un attore malintenzionato potrebbe usare xss (Cross-Site Scripting) sul sito ed eseguire una richiesta tra siti al sito abilitato per CORS per rubare informazioni.
  • Un'API non è più sicura consentendo CORS.
    • Spetta al client (browser) applicare CORS. Il server esegue la richiesta e restituisce la risposta, è il client che restituisce un errore e blocca la risposta. Ad esempio, uno degli strumenti seguenti visualizzerà la risposta del server:
  • È un modo per consentire ai browser di eseguire una richiesta XHR o Fetch API di origine incrociata che altrimenti sarebbe vietata.
    • I browser senza CORS non possono eseguire richieste tra le origini. Prima di CORS, JSONP veniva usato per aggirare questa restrizione. JSONP non usa XHR, ma usa il <script> tag per ricevere la risposta. Gli script possono essere caricati tra le origini.

La specifica CORS ha introdotto diverse nuove intestazioni HTTP che abilitano le richieste tra le origini. Se un browser supporta CORS, imposta automaticamente queste intestazioni per le richieste tra le origini. Il codice JavaScript personalizzato non è necessario per abilitare CORS.

Selezionare il pulsante PUT test nell'esempio distribuito. Intestazione Origin :

  • Fornisce il dominio del sito che effettua la richiesta.
  • È obbligatorio e deve essere diverso dall'host.

Intestazioni generali

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

Intestazioni di risposta

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

Intestazioni delle richieste

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

Nelle OPTIONS richieste, il server imposta l'intestazione Intestazioni Access-Control-Allow-Origin: {allowed origin} risposta nella risposta. Nel codice di esempio, ad esempio, la Delete [EnableCors] richiesta del pulsante OPTIONS contiene le intestazioni seguenti:

Intestazioni generali

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

Intestazioni di risposta

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

Intestazioni delle richieste

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

Nelle intestazioni response precedenti il server imposta l'intestazione Access-Control-Allow-Origin nella risposta. Il https://cors1.azurewebsites.net valore di questa intestazione corrisponde all'intestazione Origin della richiesta.

Se AllowAnyOrigin viene chiamato , Access-Control-Allow-Origin: *viene restituito il valore con caratteri jolly . AllowAnyOrigin consente qualsiasi origine.

Se la risposta non include l'intestazione Access-Control-Allow-Origin , la richiesta tra le origini ha esito negativo. In particolare, il browser non consente la richiesta. Anche se il server restituisce una risposta corretta, il browser non rende disponibile la risposta all'app client.

Il reindirizzamento HTTP a HTTPS causa ERR_INVALID_REDIRECT nella richiesta preliminare CORS

Le richieste a un endpoint che usano HTTP reindirizzate a HTTPS UseHttpsRedirection hanno esito negativo con ERR_INVALID_REDIRECT on the CORS preflight request.

I progetti API possono rifiutare le richieste HTTP anziché usarle UseHttpsRedirection per reindirizzare le richieste a HTTPS.

CORS in IIS

Quando si esegue la distribuzione in IIS, CORS deve essere eseguito prima dell'autenticazione di Windows se il server non è configurato per consentire l'accesso anonimo. Per supportare questo scenario, è necessario installare e configurare il modulo CORS IIS per l'app.

Testare CORS

Il download di esempio include codice per testare CORS. Vedere come scaricare un esempio. L'esempio è un progetto API con Razor Pages aggiunto:

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

Avviso

WithOrigins("https://localhost:<port>");deve essere usato solo per testare un'app di esempio simile al codice di esempio di download.

Di seguito ValuesController vengono forniti gli endpoint per il test:

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

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

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


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

}

MyDisplayRouteInfo viene fornito dal pacchetto NuGet Rick.Docs.Samples.RouteInfo e visualizza le informazioni sulla route.

Testare il codice di esempio precedente usando uno degli approcci seguenti:

  • Eseguire l'esempio con dotnet run usando l'URL predefinito di https://localhost:5001.
  • Eseguire l'esempio da Visual Studio con la porta impostata su 44398 per un URL di https://localhost:44398.

Uso di un browser con gli strumenti F12:

  • Selezionare il pulsante Valori ed esaminare le intestazioni nella scheda Rete .

  • Selezionare il pulsante PUT test.Select the PUT test button. Per istruzioni sulla visualizzazione della richiesta OPTIONS, vedere Visualizzare le richieste OPTIONS. Il test PUT crea due richieste, una richiesta preliminare OPTIONS e la richiesta PUT.

  • Selezionare il GetValues2 [DisableCors] pulsante per attivare una richiesta CORS non riuscita. Come accennato nel documento, la risposta restituisce 200 risultati positivi, ma la richiesta CORS non viene effettuata. Selezionare la scheda Console per visualizzare l'errore CORS. A seconda del browser, viene visualizzato un errore simile al seguente:

    L'accesso al recupero dall'origine 'https://cors1.azurewebsites.net/api/values/GetValues2' 'https://cors3.azurewebsites.net' è stato bloccato dai criteri CORS: nessuna intestazione "Access-Control-Allow-Origin" è presente nella risorsa richiesta. Se si ritiene soddisfacente una risposta opaca, impostare la modalità della richiesta 'no-cors' per recuperare la risorsa con CORS disabilitato.

Gli endpoint abilitati per CORS possono essere testati con uno strumento, ad esempio curl o Fiddler. Quando si usa uno strumento, l'origine della richiesta specificata dall'intestazione Origin deve essere diversa dall'host che riceve la richiesta. Se la richiesta non è tra le origini in base al valore dell'intestazione Origin :

  • Non è necessario che il middleware CORS elabori la richiesta.
  • Le intestazioni CORS non vengono restituite nella risposta.

Il comando seguente usa curl per emettere una richiesta OPTIONS con informazioni:

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

Testare CORS con l'attributo [EnableCors] e il metodo RequireCors

Si consideri il codice seguente che usa il routing degli endpoint per abilitare CORS per ogni endpoint 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();

Si noti che solo l'endpoint /echo usa per RequireCors consentire le richieste tra le origini usando i criteri specificati. I controller seguenti abilitano CORS usando l'attributo [EnableCors].

Di seguito TodoItems1Controller sono riportati gli endpoint per il test:

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

        return ControllerContext.MyDisplayRouteInfo(id);
    }

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

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

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

I pulsanti Delete [EnableCors] e GET [EnableCors] hanno esito positivo, perché gli endpoint hanno [EnableCors] e rispondono alle richieste preliminari. Gli altri endpoint hanno esito negativo. Il pulsante GET ha esito negativo perché JavaScript invia:

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

Di seguito TodoItems2Controller sono disponibili endpoint simili, ma include codice esplicito per rispondere alle richieste 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);
}

Il codice precedente può essere testato distribuendo l'esempio in Azure.In'elenco a discesa Controller, selezionare Preflight (Verifica preliminare) e quindi Set Controller (Imposta controller). Tutte le chiamate CORS agli TodoItems2Controller endpoint hanno esito positivo.

Risorse aggiuntive

Di Rick Anderson e Kirk Larkin

Questo articolo illustra come abilitare CORS in un'app core ASP.NET.

La sicurezza del browser impedisce a una pagina Web di effettuare richieste a un dominio diverso da quello che ha distribuito la pagina Web. Questa restrizione è nota come criteri di corrispondenza dell'origine. I criteri di corrispondenza dell'origine impediscono a un sito dannoso di accedere a dati sensibili in un altro sito. In alcuni casi, potrebbe essere necessario consentire ad altri siti di effettuare richieste tra origini all'app. Per altre informazioni, vedere l'articolo Mozilla CORS.

Condivisione di risorse tra le origini (CORS):

  • È uno standard W3C che consente a un server di rilassare i criteri di stessa origine.
  • Non è una funzionalità di sicurezza, CORS riduce la sicurezza. Un'API non è più sicura consentendo CORS. Per altre informazioni, vedere Funzionamento di CORS.
  • Consente a un server di consentire in modo esplicito alcune richieste tra le origini rifiutando altre.
  • È più sicuro e più flessibile rispetto alle tecniche precedenti, ad esempio JSONP.

Visualizzare o scaricare il codice di esempio (procedura per il download)

Stessa origine

Due URL hanno la stessa origine se hanno schemi, host e porte identici (RFC 6454).

Questi due URL hanno la stessa origine:

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

Questi URL hanno origini diverse rispetto ai due URL precedenti:

  • https://example.net: dominio diverso
  • https://www.example.com/foo.html: sottodominio diverso
  • http://example.com/foo.html: schema diverso
  • https://example.com:9000/foo.html: porta diversa

Abilitare CORS

Esistono tre modi per abilitare CORS:

L'uso dell'attributo [EnableCors] con un criterio denominato consente di controllare meglio gli endpoint che supportano CORS.

Avviso

UseCors deve essere chiamato nell'ordine corretto. Per altre informazioni, vedere Ordine del middleware. Ad esempio, UseCors deve essere chiamato prima UseResponseCaching di quando si usa UseResponseCaching.

Ogni approccio è dettagliato nelle sezioni seguenti.

CORS con criteri e middleware denominati

Il middleware CORS gestisce le richieste tra le origini. Il codice seguente applica un criterio CORS a tutti gli endpoint dell'app con le origini specificate:

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

Il codice precedente:

  • Imposta il nome del criterio su _myAllowSpecificOrigins. Il nome del criterio è arbitrario.
  • Chiama il UseCors metodo di estensione e specifica i _myAllowSpecificOrigins criteri CORS. UseCors aggiunge il middleware CORS. La chiamata a UseCors deve essere inserita dopo UseRouting, ma prima UseAuthorizationdi . Per altre informazioni, vedere Ordine del middleware.
  • Chiamate AddCors con un'espressione lambda. L'espressione lambda accetta un CorsPolicyBuilder oggetto . Le opzioni di configurazione, ad esempio WithOrigins, sono descritte più avanti in questo articolo.
  • Abilita i _myAllowSpecificOrigins criteri CORS per tutti gli endpoint controller. Vedere Routing degli endpoint per applicare un criterio CORS a endpoint specifici.
  • Quando si usa il middleware di memorizzazione nella cache delle risposte, chiamare UseCors prima UseResponseCachingdi .

Con il routing degli endpoint, il middleware CORS deve essere configurato per l'esecuzione tra le chiamate a UseRouting e UseEndpoints.

La AddCors chiamata al metodo aggiunge servizi CORS al contenitore del servizio dell'app:

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

Per altre informazioni, vedere Opzioni dei criteri CORS in questo documento.

I CorsPolicyBuilder metodi possono essere concatenati, come illustrato nel codice seguente:

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: l'URL specificato non deve contenere una barra finale (/). Se l'URL termina con /, il confronto restituisce false e non viene restituita alcuna intestazione.

Avviso

UseCors deve essere posizionato dopo UseRouting e prima UseAuthorizationdi . Ciò consente di assicurarsi che le intestazioni CORS siano incluse nella risposta sia per le chiamate autorizzate che non autorizzate.

Ordine UseCors e UseStaticFiles

In genere, UseStaticFiles viene chiamato prima UseCorsdi . Le app che usano JavaScript per recuperare i file statici tra siti devono chiamare UseCors prima UseStaticFilesdi .

CORS con criteri e middleware predefiniti

Il codice evidenziato seguente abilita i criteri CORS predefiniti:

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

Il codice precedente applica i criteri CORS predefiniti a tutti gli endpoint controller.

Abilitare Cors con il routing degli endpoint

L'abilitazione di CORS per endpoint tramite RequireCors non supporta le richieste preliminari automatiche. Per altre informazioni, vedere questo problema di GitHub e Testare CORS con il routing degli endpoint e [HttpOptions].

Con il routing degli endpoint, CORS può essere abilitato per ogni endpoint usando il RequireCors set di metodi di estensione:

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

Nel codice precedente:

  • app.UseCors abilita il middleware CORS. Poiché non è stato configurato un criterio predefinito, app.UseCors() da solo non abilita CORS.
  • Gli /echo endpoint del controller e consentono le richieste tra le origini usando i criteri specificati.
  • Gli /echo2 endpoint e Razor Pages non consentono le richieste tra le origini perché non è stato specificato alcun criterio predefinito.

L'attributo [DisableCors] non disabilita CORS abilitato dal routing degli endpoint con RequireCors.

Vedere Testare CORS con il routing degli endpoint e [HttpOptions] per istruzioni sul test del codice simile al precedente.

Abilitare CORS con attributi

L'abilitazione di CORS con l'attributo [EnableCors] e l'applicazione di un criterio denominato solo agli endpoint che richiedono CORS fornisce il controllo migliore.

L'attributo [EnableCors] offre un'alternativa all'applicazione globale di CORS. L'attributo [EnableCors] abilita CORS per gli endpoint selezionati, anziché per tutti gli endpoint:

  • [EnableCors] specifica il criterio predefinito.
  • [EnableCors("{Policy String}")] specifica un criterio denominato.

L'attributo [EnableCors] può essere applicato a:

  • Razor Pagina PageModel
  • Controller
  • Metodo di azione del controller

È possibile applicare criteri diversi ai controller, ai modelli di pagina o ai metodi di azione con l'attributo [EnableCors] . Quando l'attributo [EnableCors] viene applicato a un controller, a un modello di pagina o a un metodo di azione e CORS è abilitato nel middleware, vengono applicati entrambi i criteri. È consigliabile combinare i criteri. Usare[EnableCors] attributo o middleware, non entrambi nella stessa app.

Il codice seguente applica criteri diversi a ogni metodo:

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

Il codice seguente crea due criteri 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();

Per il controllo più dettagliato della limitazione delle richieste CORS:

  • Usare [EnableCors("MyPolicy")] con un criterio denominato.
  • Non definire un criterio predefinito.
  • Non usare il routing degli endpoint.

Il codice nella sezione successiva soddisfa l'elenco precedente.

Disabilitare CORS

L'attributo [DisableCors] non disabilita CORS abilitato dal routing degli endpoint.

Il codice seguente definisce i criteri CORS "MyPolicy":

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

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

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

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

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

app.Run();

Il codice seguente disabilita CORS per l'azione GetValues2 :

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

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

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


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

}

Il codice precedente:

Vedere Testare CORS per istruzioni sul test del codice precedente.

Opzioni dei criteri CORS

Questa sezione descrive le varie opzioni che è possibile impostare in un criterio CORS:

AddPolicy viene chiamato in Program.cs. Per alcune opzioni, può essere utile leggere prima la sezione Funzionamento di CORS .

Impostare le origini consentite

AllowAnyOrigin: consente le richieste CORS da tutte le origini con qualsiasi schema (http o https). AllowAnyOrigin non è sicuro perché qualsiasi sito Web può effettuare richieste tra le origini all'app.

Nota

Specificare AllowAnyOrigin e AllowCredentials è una configurazione non sicura e può comportare la falsificazione della richiesta intersito. Il servizio CORS restituisce una risposta CORS non valida quando un'app è configurata con entrambi i metodi.

AllowAnyOrigin influisce sulle richieste preliminari e sull'intestazione Access-Control-Allow-Origin . Per altre informazioni, vedere la sezione Richieste preliminari .

SetIsOriginAllowedToAllowWildcardSubdomains: imposta la IsOriginAllowed proprietà del criterio come funzione che consente alle origini di trovare una corrispondenza con un dominio con caratteri jolly configurato durante la valutazione se l'origine è consentita.

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

Impostare i metodi HTTP consentiti

AllowAnyMethod:

  • Consente qualsiasi metodo HTTP:
  • Influisce sulle richieste preliminari e sull'intestazione Access-Control-Allow-Methods . Per altre informazioni, vedere la sezione Richieste preliminari .

Impostare le intestazioni di richiesta consentite

Per consentire l'invio di intestazioni specifiche in una richiesta CORS, denominata intestazioni di richiesta autore, chiamare WithHeaders e specificare le intestazioni consentite:

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

Per consentire tutte le intestazioni di richiesta dell'autore, chiamare 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 influisce sulle richieste preliminari e sull'intestazione Access-Control-Request-Headers . Per altre informazioni, vedere la sezione Richieste preliminari .

Un criterio middleware CORS corrisponde a intestazioni specifiche specificate da WithHeaders è possibile solo quando le intestazioni inviate in Access-Control-Request-Headers corrispondono esattamente alle intestazioni indicate in WithHeaders.

Si consideri, ad esempio, un'app configurata come segue:

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

Il middleware CORS rifiuta una richiesta preliminare con l'intestazione di richiesta seguente perché Content-Language (HeaderNames.ContentLanguage) non è elencato in WithHeaders:

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

L'app restituisce una risposta 200 OK , ma non invia di nuovo le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra le origini.

Impostare le intestazioni di risposta esposte

Per impostazione predefinita, il browser non espone tutte le intestazioni di risposta all'app. Per altre informazioni, vedere W3C Cross-Origin Resource Sharing (Terminologia): Intestazione di risposta semplice.

Le intestazioni di risposta disponibili per impostazione predefinita sono:

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

La specifica CORS chiama queste intestazioni semplici intestazioni di risposta. Per rendere disponibili altre intestazioni all'app, chiamare 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();

Credenziali nelle richieste tra le origini

Le credenziali richiedono una gestione speciale in una richiesta CORS. Per impostazione predefinita, il browser non invia le credenziali con una richiesta tra le origini. Le credenziali includono cookie e schemi di autenticazione HTTP. Per inviare le credenziali con una richiesta tra le origini, il client deve impostare su XMLHttpRequest.withCredentials true.

Uso XMLHttpRequest diretto:

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

Uso di jQuery:

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

Uso dell'API Fetch:

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

Il server deve consentire le credenziali. Per consentire le credenziali tra le origini, chiamare AllowCredentials:

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddControllers();

var app = builder.Build();

La risposta HTTP include un'intestazione Access-Control-Allow-Credentials che indica al browser che il server consente le credenziali per una richiesta tra le origini.

Se il browser invia credenziali ma la risposta non include un'intestazione valida Access-Control-Allow-Credentials , il browser non espone la risposta all'app e la richiesta tra le origini ha esito negativo.

Consentire le credenziali tra le origini è un rischio per la sicurezza. Un sito Web in un altro dominio può inviare le credenziali di un utente connesso all'app per conto dell'utente senza conoscere l'utente.

La specifica CORS indica inoltre che l'impostazione delle origini su "*" (tutte le origini) non è valida se l'intestazione Access-Control-Allow-Credentials è presente.

Richieste preliminari

Per alcune richieste CORS, il browser invia una richiesta OPTIONS aggiuntiva prima di effettuare la richiesta effettiva. Questa richiesta viene chiamata richiesta preliminare. Il browser può ignorare la richiesta preliminare se sono soddisfatte tutte le condizioni seguenti:

  • Il metodo di richiesta è GET, HEAD o POST.
  • L'app non imposta intestazioni di richiesta diverse da Accept, Accept-Language, Content-LanguageContent-Type, o Last-Event-ID.
  • L'intestazione Content-Type , se impostata, ha uno dei valori seguenti:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

La regola sulle intestazioni di richiesta impostata per la richiesta client si applica alle intestazioni impostate dall'app chiamando setRequestHeader sull'oggetto XMLHttpRequest . La specifica CORS chiama queste intestazioni le intestazioni di richiesta dell'autore. La regola non si applica alle intestazioni che il browser può impostare, ad esempio User-Agent, Hosto Content-Length.

Di seguito è riportata una risposta di esempio simile alla richiesta preliminare effettuata dal pulsante [Put test] nella sezione Test CORS di questo documento.

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

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

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

La richiesta preliminare usa il metodo HTTP OPTIONS . Può includere le intestazioni seguenti:

Se la richiesta preliminare viene negata, l'app restituisce una 200 OK risposta ma non imposta le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra le origini. Per un esempio di richiesta preliminare negata, vedere la sezione Test CORS di questo documento.

Usando gli strumenti F12, l'app console mostra un errore simile a uno dei seguenti, a seconda del browser:

  • Firefox: Richiesta tra le origini bloccate: lo stesso criterio di origine non consente la lettura della risorsa remota in https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Motivo: richiesta CORS non riuscita). Altre informazioni
  • Basato su Chromium: l'accesso al recupero all'origine 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' dall'origine 'https://cors3.azurewebsites.net' è stato bloccato dai criteri CORS: la risposta alla richiesta preliminare non supera il controllo di accesso: nessuna intestazione 'Access-Control-Allow-Origin' è presente nella risorsa richiesta. Se si ritiene soddisfacente una risposta opaca, impostare la modalità della richiesta 'no-cors' per recuperare la risorsa con CORS disabilitato.

Per consentire intestazioni specifiche, chiamare 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();

Per consentire tutte le intestazioni di richiesta dell'autore, chiamare 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();

I browser non sono coerenti nel modo in cui impostano Access-Control-Request-Headers. In caso affermativo:

  • Le intestazioni sono impostate su un valore diverso da "*"
  • AllowAnyHeader viene chiamato: includere almeno Accept, Content-Typee Origin, più eventuali intestazioni personalizzate che si desidera supportare.

Codice di richiesta preliminare automatico

Quando vengono applicati i criteri CORS:

  • A livello globale chiamando app.UseCors in Program.cs.
  • Uso dell'attributo [EnableCors] .

ASP.NET Core risponde alla richiesta OPTIONS preliminare.

L'abilitazione di CORS in base all'endpoint con RequireCors attualmente non supporta le richieste preliminari automatiche.

La sezione Test CORS di questo documento illustra questo comportamento.

Attributo [HttpOptions] per le richieste preliminari

Quando CORS è abilitato con i criteri appropriati, ASP.NET Core risponde in genere alle richieste preliminari CORS automaticamente. In alcuni scenari, questo potrebbe non essere il caso. Ad esempio, usando CORS con il routing degli endpoint.

Il codice seguente usa l'attributo [HttpOptions] per creare endpoint per le richieste 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);
    }

Vedere Testare CORS con il routing degli endpoint e [HttpOptions] per istruzioni sul test del codice precedente.

Impostare l'ora di scadenza preliminare

L'intestazione Access-Control-Max-Age specifica per quanto tempo è possibile memorizzare nella cache la risposta alla richiesta preliminare. Per impostare questa intestazione, chiamare 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();

Funzionamento di CORS

Questa sezione descrive cosa accade in una richiesta CORS a livello di messaggi HTTP.

  • CORS non è una funzionalità di sicurezza. CORS è uno standard W3C che consente a un server di ridurre i criteri di stessa origine.
    • Ad esempio, un attore malintenzionato potrebbe usare xss (Cross-Site Scripting) sul sito ed eseguire una richiesta tra siti al sito abilitato per CORS per rubare informazioni.
  • Un'API non è più sicura consentendo CORS.
    • Spetta al client (browser) applicare CORS. Il server esegue la richiesta e restituisce la risposta, è il client che restituisce un errore e blocca la risposta. Ad esempio, uno degli strumenti seguenti visualizzerà la risposta del server:
  • È un modo per consentire ai browser di eseguire una richiesta XHR o Fetch API di origine incrociata che altrimenti sarebbe vietata.
    • I browser senza CORS non possono eseguire richieste tra le origini. Prima di CORS, JSONP veniva usato per aggirare questa restrizione. JSONP non usa XHR, ma usa il <script> tag per ricevere la risposta. Gli script possono essere caricati tra le origini.

La specifica CORS ha introdotto diverse nuove intestazioni HTTP che abilitano le richieste tra le origini. Se un browser supporta CORS, imposta automaticamente queste intestazioni per le richieste tra le origini. Il codice JavaScript personalizzato non è necessario per abilitare CORS.

Di seguito è riportato un esempio di richiesta tra le origini dal pulsante di test Values a https://cors1.azurewebsites.net/api/values. Intestazione Origin :

  • Fornisce il dominio del sito che effettua la richiesta.
  • È obbligatorio e deve essere diverso dall'host.

Intestazioni generali

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

Intestazioni di risposta

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

Intestazioni delle richieste

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

Nelle OPTIONS richieste, il server imposta l'intestazione Intestazioni Access-Control-Allow-Origin: {allowed origin} risposta nella risposta. Ad esempio, l'esempio distribuito, la richiesta del pulsante OPTIONS Delete [EnableCors] contiene le intestazioni seguenti:

Intestazioni generali

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

Intestazioni di risposta

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

Intestazioni delle richieste

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

Nelle intestazioni response precedenti il server imposta l'intestazione Access-Control-Allow-Origin nella risposta. Il https://cors1.azurewebsites.net valore di questa intestazione corrisponde all'intestazione Origin della richiesta.

Se AllowAnyOrigin viene chiamato , Access-Control-Allow-Origin: *viene restituito il valore con caratteri jolly . AllowAnyOrigin consente qualsiasi origine.

Se la risposta non include l'intestazione Access-Control-Allow-Origin , la richiesta tra le origini ha esito negativo. In particolare, il browser non consente la richiesta. Anche se il server restituisce una risposta corretta, il browser non rende disponibile la risposta all'app client.

Il reindirizzamento HTTP a HTTPS causa ERR_INVALID_REDIRECT nella richiesta preliminare CORS

Le richieste a un endpoint che usano HTTP reindirizzate a HTTPS UseHttpsRedirection hanno esito negativo con ERR_INVALID_REDIRECT on the CORS preflight request.

I progetti API possono rifiutare le richieste HTTP anziché usarle UseHttpsRedirection per reindirizzare le richieste a HTTPS.

Visualizzare le richieste OPTIONS

Per impostazione predefinita, i browser Chrome e Edge non visualizzano le richieste OPTIONS nella scheda di rete degli strumenti F12. Per visualizzare le richieste OPTIONS nei browser seguenti:

  • chrome://flags/#out-of-blink-cors oppure edge://flags/#out-of-blink-cors
  • disabilitare il flag.
  • riavviare.

Firefox mostra le richieste OPTIONS per impostazione predefinita.

CORS in IIS

Quando si esegue la distribuzione in IIS, CORS deve essere eseguito prima dell'autenticazione di Windows se il server non è configurato per consentire l'accesso anonimo. Per supportare questo scenario, è necessario installare e configurare il modulo CORS IIS per l'app.

Testare CORS

Il download di esempio include codice per testare CORS. Vedere come scaricare un esempio. L'esempio è un progetto API con Razor Pages aggiunto:

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

Avviso

WithOrigins("https://localhost:<port>");deve essere usato solo per testare un'app di esempio simile al codice di esempio di download.

Di seguito ValuesController vengono forniti gli endpoint per il test:

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

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

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


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

}

MyDisplayRouteInfo viene fornito dal pacchetto NuGet Rick.Docs.Samples.RouteInfo e visualizza le informazioni sulla route.

Testare il codice di esempio precedente usando uno degli approcci seguenti:

  • Eseguire l'esempio con dotnet run usando l'URL predefinito di https://localhost:5001.
  • Eseguire l'esempio da Visual Studio con la porta impostata su 44398 per un URL di https://localhost:44398.

Uso di un browser con gli strumenti F12:

  • Selezionare il pulsante Valori ed esaminare le intestazioni nella scheda Rete .

  • Selezionare il pulsante PUT test.Select the PUT test button. Per istruzioni sulla visualizzazione della richiesta OPTIONS, vedere Visualizzare le richieste OPTIONS. Il test PUT crea due richieste, una richiesta preliminare OPTIONS e la richiesta PUT.

  • Selezionare il GetValues2 [DisableCors] pulsante per attivare una richiesta CORS non riuscita. Come accennato nel documento, la risposta restituisce 200 risultati positivi, ma la richiesta CORS non viene effettuata. Selezionare la scheda Console per visualizzare l'errore CORS. A seconda del browser, viene visualizzato un errore simile al seguente:

    L'accesso al recupero dall'origine 'https://cors1.azurewebsites.net/api/values/GetValues2' 'https://cors3.azurewebsites.net' è stato bloccato dai criteri CORS: nessuna intestazione "Access-Control-Allow-Origin" è presente nella risorsa richiesta. Se si ritiene soddisfacente una risposta opaca, impostare la modalità della richiesta 'no-cors' per recuperare la risorsa con CORS disabilitato.

Gli endpoint abilitati per CORS possono essere testati con uno strumento, ad esempio curl o Fiddler. Quando si usa uno strumento, l'origine della richiesta specificata dall'intestazione Origin deve essere diversa dall'host che riceve la richiesta. Se la richiesta non è tra le origini in base al valore dell'intestazione Origin :

  • Non è necessario che il middleware CORS elabori la richiesta.
  • Le intestazioni CORS non vengono restituite nella risposta.

Il comando seguente usa curl per emettere una richiesta OPTIONS con informazioni:

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

Testare CORS con il routing degli endpoint e [HttpOptions]

L'abilitazione di CORS in base all'endpoint con RequireCors attualmente non supporta le richieste preliminari automatiche. Considerare il codice seguente che usa il routing degli endpoint per abilitare 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();

Di seguito TodoItems1Controller sono riportati gli endpoint per il test:

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

        return ControllerContext.MyDisplayRouteInfo(id);
    }

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

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

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

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

Testare il codice precedente dalla pagina di test (https://cors1.azurewebsites.net/test?number=1) dell'esempio distribuito.

I pulsanti Delete [EnableCors] e GET [EnableCors] hanno esito positivo, perché gli endpoint hanno [EnableCors] e rispondono alle richieste preliminari. Gli altri endpoint hanno esito negativo. Il pulsante GET ha esito negativo perché JavaScript invia:

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

Di seguito TodoItems2Controller sono disponibili endpoint simili, ma include codice esplicito per rispondere alle richieste 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);
}

Il codice precedente può essere testato distribuendo l'esempio in Azure.In'elenco a discesa Controller, selezionare Preflight (Verifica preliminare) e quindi Set Controller (Imposta controller). Tutte le chiamate CORS agli TodoItems2Controller endpoint hanno esito positivo.

Risorse aggiuntive

Di Rick Anderson e Kirk Larkin

Questo articolo illustra come abilitare CORS in un'app core ASP.NET.

La sicurezza del browser impedisce a una pagina Web di effettuare richieste a un dominio diverso da quello che ha distribuito la pagina Web. Questa restrizione è nota come criteri di corrispondenza dell'origine. I criteri di corrispondenza dell'origine impediscono a un sito dannoso di accedere a dati sensibili in un altro sito. In alcuni casi, potrebbe essere necessario consentire ad altri siti di effettuare richieste tra origini all'app. Per altre informazioni, vedere l'articolo Mozilla CORS.

Condivisione di risorse tra le origini (CORS):

  • È uno standard W3C che consente a un server di rilassare i criteri di stessa origine.
  • Non è una funzionalità di sicurezza, CORS riduce la sicurezza. Un'API non è più sicura consentendo CORS. Per altre informazioni, vedere Funzionamento di CORS.
  • Consente a un server di consentire in modo esplicito alcune richieste tra le origini rifiutando altre.
  • È più sicuro e più flessibile rispetto alle tecniche precedenti, ad esempio JSONP.

Visualizzare o scaricare il codice di esempio (procedura per il download)

Stessa origine

Due URL hanno la stessa origine se hanno schemi, host e porte identici (RFC 6454).

Questi due URL hanno la stessa origine:

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

Questi URL hanno origini diverse rispetto ai due URL precedenti:

  • https://example.net: dominio diverso
  • https://www.example.com/foo.html: sottodominio diverso
  • http://example.com/foo.html: schema diverso
  • https://example.com:9000/foo.html: porta diversa

Abilitare CORS

Esistono tre modi per abilitare CORS:

L'uso dell'attributo [EnableCors] con un criterio denominato consente di controllare meglio gli endpoint che supportano CORS.

Avviso

UseCors deve essere chiamato nell'ordine corretto. Per altre informazioni, vedere Ordine del middleware. Ad esempio, UseCors deve essere chiamato prima UseResponseCaching di quando si usa UseResponseCaching.

Ogni approccio è dettagliato nelle sezioni seguenti.

CORS con criteri e middleware denominati

Il middleware CORS gestisce le richieste tra le origini. Il codice seguente applica un criterio CORS a tutti gli endpoint dell'app con le origini specificate:

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

Il codice precedente:

  • Imposta il nome del criterio su _myAllowSpecificOrigins. Il nome del criterio è arbitrario.
  • Chiama il UseCors metodo di estensione e specifica i _myAllowSpecificOrigins criteri CORS. UseCors aggiunge il middleware CORS. La chiamata a UseCors deve essere inserita dopo UseRouting, ma prima UseAuthorizationdi . Per altre informazioni, vedere Ordine del middleware.
  • Chiamate AddCors con un'espressione lambda. L'espressione lambda accetta un CorsPolicyBuilder oggetto . Le opzioni di configurazione, ad esempio WithOrigins, sono descritte più avanti in questo articolo.
  • Abilita i _myAllowSpecificOrigins criteri CORS per tutti gli endpoint controller. Vedere Routing degli endpoint per applicare un criterio CORS a endpoint specifici.
  • Quando si usa il middleware di memorizzazione nella cache delle risposte, chiamare UseCors prima UseResponseCachingdi .

Con il routing degli endpoint, il middleware CORS deve essere configurato per l'esecuzione tra le chiamate a UseRouting e UseEndpoints.

Per istruzioni sul test del codice simile al codice precedente, vedere Testare CORS .

La AddCors chiamata al metodo aggiunge servizi CORS al contenitore del servizio dell'app:

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

Per altre informazioni, vedere Opzioni dei criteri CORS in questo documento.

I CorsPolicyBuilder metodi possono essere concatenati, come illustrato nel codice seguente:

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: l'URL specificato non deve contenere una barra finale (/). Se l'URL termina con /, il confronto restituisce false e non viene restituita alcuna intestazione.

CORS con criteri e middleware predefiniti

Il codice evidenziato seguente abilita i criteri CORS predefiniti:

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

Il codice precedente applica i criteri CORS predefiniti a tutti gli endpoint controller.

Abilitare Cors con il routing degli endpoint

L'abilitazione di CORS per endpoint tramite RequireCors non supporta le richieste preliminari automatiche. Per altre informazioni, vedere questo problema di GitHub e Testare CORS con il routing degli endpoint e [HttpOptions].

Con il routing degli endpoint, CORS può essere abilitato per ogni endpoint usando il RequireCors set di metodi di estensione:

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

Nel codice precedente:

  • app.UseCors abilita il middleware CORS. Poiché non è stato configurato un criterio predefinito, app.UseCors() da solo non abilita CORS.
  • Gli /echo endpoint del controller e consentono le richieste tra le origini usando i criteri specificati.
  • Gli /echo2 endpoint e Razor Pages non consentono le richieste tra le origini perché non è stato specificato alcun criterio predefinito.

L'attributo [DisableCors] non disabilita CORS abilitato dal routing degli endpoint con RequireCors.

Vedere Testare CORS con il routing degli endpoint e [HttpOptions] per istruzioni sul test del codice simile al precedente.

Abilitare CORS con attributi

L'abilitazione di CORS con l'attributo [EnableCors] e l'applicazione di un criterio denominato solo agli endpoint che richiedono CORS fornisce il controllo migliore.

L'attributo [EnableCors] offre un'alternativa all'applicazione globale di CORS. L'attributo [EnableCors] abilita CORS per gli endpoint selezionati, anziché per tutti gli endpoint:

  • [EnableCors] specifica il criterio predefinito.
  • [EnableCors("{Policy String}")] specifica un criterio denominato.

L'attributo [EnableCors] può essere applicato a:

  • Razor Pagina PageModel
  • Controller
  • Metodo di azione del controller

È possibile applicare criteri diversi ai controller, ai modelli di pagina o ai metodi di azione con l'attributo [EnableCors] . Quando l'attributo [EnableCors] viene applicato a un controller, a un modello di pagina o a un metodo di azione e CORS è abilitato nel middleware, vengono applicati entrambi i criteri. È consigliabile combinare i criteri. Usare[EnableCors] attributo o middleware, non entrambi nella stessa app.

Il codice seguente applica criteri diversi a ogni metodo:

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

Il codice seguente crea due criteri 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();
        });
    }
}

Per il controllo più dettagliato della limitazione delle richieste CORS:

  • Usare [EnableCors("MyPolicy")] con un criterio denominato.
  • Non definire un criterio predefinito.
  • Non usare il routing degli endpoint.

Il codice nella sezione successiva soddisfa l'elenco precedente.

Per istruzioni sul test del codice simile al codice precedente, vedere Testare CORS .

Disabilitare CORS

L'attributo [DisableCors] non disabilita CORS abilitato dal routing degli endpoint.

Il codice seguente definisce i criteri CORS "MyPolicy":

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

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

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

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

        app.UseCors();

        app.UseAuthorization();

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

Il codice seguente disabilita CORS per l'azione GetValues2 :

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

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

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


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

}

Il codice precedente:

Vedere Testare CORS per istruzioni sul test del codice precedente.

Opzioni dei criteri CORS

Questa sezione descrive le varie opzioni che è possibile impostare in un criterio CORS:

AddPolicy viene chiamato in Startup.ConfigureServices. Per alcune opzioni, può essere utile leggere prima la sezione Funzionamento di CORS .

Impostare le origini consentite

AllowAnyOrigin: consente le richieste CORS da tutte le origini con qualsiasi schema (http o https). AllowAnyOrigin non è sicuro perché qualsiasi sito Web può effettuare richieste tra le origini all'app.

Nota

Specificare AllowAnyOrigin e AllowCredentials è una configurazione non sicura e può comportare la falsificazione della richiesta intersito. Il servizio CORS restituisce una risposta CORS non valida quando un'app è configurata con entrambi i metodi.

AllowAnyOrigin influisce sulle richieste preliminari e sull'intestazione Access-Control-Allow-Origin . Per altre informazioni, vedere la sezione Richieste preliminari .

SetIsOriginAllowedToAllowWildcardSubdomains: imposta la IsOriginAllowed proprietà del criterio come funzione che consente alle origini di trovare una corrispondenza con un dominio con caratteri jolly configurato durante la valutazione se l'origine è consentita.

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

Impostare i metodi HTTP consentiti

AllowAnyMethod:

  • Consente qualsiasi metodo HTTP:
  • Influisce sulle richieste preliminari e sull'intestazione Access-Control-Allow-Methods . Per altre informazioni, vedere la sezione Richieste preliminari .

Impostare le intestazioni di richiesta consentite

Per consentire l'invio di intestazioni specifiche in una richiesta CORS, denominata intestazioni di richiesta autore, chiamare WithHeaders e specificare le intestazioni consentite:

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

Per consentire tutte le intestazioni di richiesta dell'autore, chiamare AllowAnyHeader:

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

AllowAnyHeader influisce sulle richieste preliminari e sull'intestazione Access-Control-Request-Headers . Per altre informazioni, vedere la sezione Richieste preliminari .

Un criterio middleware CORS corrisponde a intestazioni specifiche specificate da WithHeaders è possibile solo quando le intestazioni inviate in Access-Control-Request-Headers corrispondono esattamente alle intestazioni indicate in WithHeaders.

Si consideri, ad esempio, un'app configurata come segue:

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

Il middleware CORS rifiuta una richiesta preliminare con l'intestazione di richiesta seguente perché Content-Language (HeaderNames.ContentLanguage) non è elencato in WithHeaders:

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

L'app restituisce una risposta 200 OK , ma non invia di nuovo le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra le origini.

Impostare le intestazioni di risposta esposte

Per impostazione predefinita, il browser non espone tutte le intestazioni di risposta all'app. Per altre informazioni, vedere W3C Cross-Origin Resource Sharing (Terminologia): Intestazione di risposta semplice.

Le intestazioni di risposta disponibili per impostazione predefinita sono:

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

La specifica CORS chiama queste intestazioni semplici intestazioni di risposta. Per rendere disponibili altre intestazioni all'app, chiamare WithExposedHeaders:

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

Credenziali nelle richieste tra le origini

Le credenziali richiedono una gestione speciale in una richiesta CORS. Per impostazione predefinita, il browser non invia le credenziali con una richiesta tra le origini. Le credenziali includono cookie e schemi di autenticazione HTTP. Per inviare le credenziali con una richiesta tra le origini, il client deve impostare su XMLHttpRequest.withCredentials true.

Uso XMLHttpRequest diretto:

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

Uso di jQuery:

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

Uso dell'API Fetch:

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

Il server deve consentire le credenziali. Per consentire le credenziali tra le origini, chiamare AllowCredentials:

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

La risposta HTTP include un'intestazione Access-Control-Allow-Credentials che indica al browser che il server consente le credenziali per una richiesta tra le origini.

Se il browser invia credenziali ma la risposta non include un'intestazione valida Access-Control-Allow-Credentials , il browser non espone la risposta all'app e la richiesta tra le origini ha esito negativo.

Consentire le credenziali tra le origini è un rischio per la sicurezza. Un sito Web in un altro dominio può inviare le credenziali di un utente connesso all'app per conto dell'utente senza conoscere l'utente.

La specifica CORS indica inoltre che l'impostazione delle origini su "*" (tutte le origini) non è valida se l'intestazione Access-Control-Allow-Credentials è presente.

Richieste preliminari

Per alcune richieste CORS, il browser invia una richiesta OPTIONS aggiuntiva prima di effettuare la richiesta effettiva. Questa richiesta viene chiamata richiesta preliminare. Il browser può ignorare la richiesta preliminare se sono soddisfatte tutte le condizioni seguenti:

  • Il metodo di richiesta è GET, HEAD o POST.
  • L'app non imposta intestazioni di richiesta diverse da Accept, Accept-Language, Content-LanguageContent-Type, o Last-Event-ID.
  • L'intestazione Content-Type , se impostata, ha uno dei valori seguenti:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

La regola sulle intestazioni di richiesta impostata per la richiesta client si applica alle intestazioni impostate dall'app chiamando setRequestHeader sull'oggetto XMLHttpRequest . La specifica CORS chiama queste intestazioni le intestazioni di richiesta dell'autore. La regola non si applica alle intestazioni che il browser può impostare, ad esempio User-Agent, Hosto Content-Length.

Di seguito è riportata una risposta di esempio simile alla richiesta preliminare effettuata dal pulsante [Put test] nella sezione Test CORS di questo documento.

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

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

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

La richiesta preliminare usa il metodo HTTP OPTIONS . Può includere le intestazioni seguenti:

Se la richiesta preliminare viene negata, l'app restituisce una 200 OK risposta ma non imposta le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra le origini. Per un esempio di richiesta preliminare negata, vedere la sezione Test CORS di questo documento.

Usando gli strumenti F12, l'app console mostra un errore simile a uno dei seguenti, a seconda del browser:

  • Firefox: Richiesta tra le origini bloccate: lo stesso criterio di origine non consente la lettura della risorsa remota in https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Motivo: richiesta CORS non riuscita). Altre informazioni
  • Basato su Chromium: l'accesso al recupero all'origine 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' dall'origine 'https://cors3.azurewebsites.net' è stato bloccato dai criteri CORS: la risposta alla richiesta preliminare non supera il controllo di accesso: nessuna intestazione 'Access-Control-Allow-Origin' è presente nella risorsa richiesta. Se si ritiene soddisfacente una risposta opaca, impostare la modalità della richiesta 'no-cors' per recuperare la risorsa con CORS disabilitato.

Per consentire intestazioni specifiche, chiamare WithHeaders:

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

Per consentire tutte le intestazioni di richiesta dell'autore, chiamare AllowAnyHeader:

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

I browser non sono coerenti nel modo in cui impostano Access-Control-Request-Headers. In caso affermativo:

  • Le intestazioni sono impostate su un valore diverso da "*"
  • AllowAnyHeader viene chiamato: includere almeno Accept, Content-Typee Origin, più eventuali intestazioni personalizzate che si desidera supportare.

Codice di richiesta preliminare automatico

Quando vengono applicati i criteri CORS:

  • A livello globale chiamando app.UseCors in Startup.Configure.
  • Uso dell'attributo [EnableCors] .

ASP.NET Core risponde alla richiesta OPTIONS preliminare.

L'abilitazione di CORS in base all'endpoint con RequireCors attualmente non supporta le richieste preliminari automatiche.

La sezione Test CORS di questo documento illustra questo comportamento.

Attributo [HttpOptions] per le richieste preliminari

Quando CORS è abilitato con i criteri appropriati, ASP.NET Core risponde in genere alle richieste preliminari CORS automaticamente. In alcuni scenari, questo potrebbe non essere il caso. Ad esempio, usando CORS con il routing degli endpoint.

Il codice seguente usa l'attributo [HttpOptions] per creare endpoint per le richieste 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);
    }

Vedere Testare CORS con il routing degli endpoint e [HttpOptions] per istruzioni sul test del codice precedente.

Impostare l'ora di scadenza preliminare

L'intestazione Access-Control-Max-Age specifica per quanto tempo è possibile memorizzare nella cache la risposta alla richiesta preliminare. Per impostare questa intestazione, chiamare SetPreflightMaxAge:

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

Funzionamento di CORS

Questa sezione descrive cosa accade in una richiesta CORS a livello di messaggi HTTP.

  • CORS non è una funzionalità di sicurezza. CORS è uno standard W3C che consente a un server di ridurre i criteri di stessa origine.
    • Ad esempio, un attore malintenzionato potrebbe usare xss (Cross-Site Scripting) sul sito ed eseguire una richiesta tra siti al sito abilitato per CORS per rubare informazioni.
  • Un'API non è più sicura consentendo CORS.
    • Spetta al client (browser) applicare CORS. Il server esegue la richiesta e restituisce la risposta, è il client che restituisce un errore e blocca la risposta. Ad esempio, uno degli strumenti seguenti visualizzerà la risposta del server:
  • È un modo per consentire ai browser di eseguire una richiesta XHR o Fetch API di origine incrociata che altrimenti sarebbe vietata.
    • I browser senza CORS non possono eseguire richieste tra le origini. Prima di CORS, JSONP veniva usato per aggirare questa restrizione. JSONP non usa XHR, ma usa il <script> tag per ricevere la risposta. Gli script possono essere caricati tra le origini.

La specifica CORS ha introdotto diverse nuove intestazioni HTTP che abilitano le richieste tra le origini. Se un browser supporta CORS, imposta automaticamente queste intestazioni per le richieste tra le origini. Il codice JavaScript personalizzato non è necessario per abilitare CORS.

Di seguito è riportato un esempio di richiesta tra le origini dal pulsante di test Values a https://cors1.azurewebsites.net/api/values. Intestazione Origin :

  • Fornisce il dominio del sito che effettua la richiesta.
  • È obbligatorio e deve essere diverso dall'host.

Intestazioni generali

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

Intestazioni di risposta

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

Intestazioni delle richieste

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

Nelle OPTIONS richieste, il server imposta l'intestazione Intestazioni Access-Control-Allow-Origin: {allowed origin} risposta nella risposta. Ad esempio, l'esempio distribuito, la richiesta del pulsante OPTIONS Delete [EnableCors] contiene le intestazioni seguenti:

Intestazioni generali

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

Intestazioni di risposta

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

Intestazioni delle richieste

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

Nelle intestazioni response precedenti il server imposta l'intestazione Access-Control-Allow-Origin nella risposta. Il https://cors1.azurewebsites.net valore di questa intestazione corrisponde all'intestazione Origin della richiesta.

Se AllowAnyOrigin viene chiamato , Access-Control-Allow-Origin: *viene restituito il valore con caratteri jolly . AllowAnyOrigin consente qualsiasi origine.

Se la risposta non include l'intestazione Access-Control-Allow-Origin , la richiesta tra le origini ha esito negativo. In particolare, il browser non consente la richiesta. Anche se il server restituisce una risposta corretta, il browser non rende disponibile la risposta all'app client.

Visualizzare le richieste OPTIONS

Per impostazione predefinita, i browser Chrome e Edge non visualizzano le richieste OPTIONS nella scheda di rete degli strumenti F12. Per visualizzare le richieste OPTIONS nei browser seguenti:

  • chrome://flags/#out-of-blink-cors oppure edge://flags/#out-of-blink-cors
  • disabilitare il flag.
  • riavviare.

Firefox mostra le richieste OPTIONS per impostazione predefinita.

CORS in IIS

Quando si esegue la distribuzione in IIS, CORS deve essere eseguito prima dell'autenticazione di Windows se il server non è configurato per consentire l'accesso anonimo. Per supportare questo scenario, è necessario installare e configurare il modulo CORS IIS per l'app.

Testare CORS

Il download di esempio include codice per testare CORS. Vedere come scaricare un esempio. L'esempio è un progetto API con Razor Pages aggiunto:

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

Avviso

WithOrigins("https://localhost:<port>");deve essere usato solo per testare un'app di esempio simile al codice di esempio di download.

Di seguito ValuesController vengono forniti gli endpoint per il test:

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

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

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


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

}

MyDisplayRouteInfo viene fornito dal pacchetto NuGet Rick.Docs.Samples.RouteInfo e visualizza le informazioni sulla route.

Testare il codice di esempio precedente usando uno degli approcci seguenti:

  • Eseguire l'esempio con dotnet run usando l'URL predefinito di https://localhost:5001.
  • Eseguire l'esempio da Visual Studio con la porta impostata su 44398 per un URL di https://localhost:44398.

Uso di un browser con gli strumenti F12:

  • Selezionare il pulsante Valori ed esaminare le intestazioni nella scheda Rete .

  • Selezionare il pulsante PUT test.Select the PUT test button. Per istruzioni sulla visualizzazione della richiesta OPTIONS, vedere Visualizzare le richieste OPTIONS. Il test PUT crea due richieste, una richiesta preliminare OPTIONS e la richiesta PUT.

  • Selezionare il GetValues2 [DisableCors] pulsante per attivare una richiesta CORS non riuscita. Come accennato nel documento, la risposta restituisce 200 risultati positivi, ma la richiesta CORS non viene effettuata. Selezionare la scheda Console per visualizzare l'errore CORS. A seconda del browser, viene visualizzato un errore simile al seguente:

    L'accesso al recupero dall'origine 'https://cors1.azurewebsites.net/api/values/GetValues2' 'https://cors3.azurewebsites.net' è stato bloccato dai criteri CORS: nessuna intestazione "Access-Control-Allow-Origin" è presente nella risorsa richiesta. Se si ritiene soddisfacente una risposta opaca, impostare la modalità della richiesta 'no-cors' per recuperare la risorsa con CORS disabilitato.

Gli endpoint abilitati per CORS possono essere testati con uno strumento, ad esempio curl o Fiddler. Quando si usa uno strumento, l'origine della richiesta specificata dall'intestazione Origin deve essere diversa dall'host che riceve la richiesta. Se la richiesta non è tra le origini in base al valore dell'intestazione Origin :

  • Non è necessario che il middleware CORS elabori la richiesta.
  • Le intestazioni CORS non vengono restituite nella risposta.

Il comando seguente usa curl per emettere una richiesta OPTIONS con informazioni:

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

Testare CORS con il routing degli endpoint e [HttpOptions]

L'abilitazione di CORS in base all'endpoint con RequireCors attualmente non supporta le richieste preliminari automatiche. Considerare il codice seguente che usa il routing degli endpoint per abilitare 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();
        });
    }
}

Di seguito TodoItems1Controller sono riportati gli endpoint per il test:

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

        return ControllerContext.MyDisplayRouteInfo(id);
    }

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

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

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

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

Testare il codice precedente dalla pagina di test (https://cors1.azurewebsites.net/test?number=1) dell'esempio distribuito.

I pulsanti Delete [EnableCors] e GET [EnableCors] hanno esito positivo, perché gli endpoint hanno [EnableCors] e rispondono alle richieste preliminari. Gli altri endpoint hanno esito negativo. Il pulsante GET ha esito negativo perché JavaScript invia:

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

Di seguito TodoItems2Controller sono disponibili endpoint simili, ma include codice esplicito per rispondere alle richieste 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);
}

Il codice precedente può essere testato distribuendo l'esempio in Azure.In'elenco a discesa Controller, selezionare Preflight (Verifica preliminare) e quindi Set Controller (Imposta controller). Tutte le chiamate CORS agli TodoItems2Controller endpoint hanno esito positivo.

Risorse aggiuntive