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

Note

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

Warning

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 10 di questo articolo.

Di Rick Anderson e Kirk Larkin

Questo articolo illustra come Cross-Origin Resource Sharing (CORS) viene abilitata 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 è chiamata criterio della stessa origine. La policy della stessa origine impedisce a un sito dannoso di leggere dati sensibili da 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 delle risorse tra origini diverse (CORS):

  • È uno standard W3C che consente a un server di allentare il criterio della stessa origine.
  • Non è una funzione di sicurezza, CORS allenta la sicurezza. Un'API non è più sicura consentendo CORS. Per altre informazioni, vedere Funzionamento di CORS.
  • Consente a un server di autorizzare esplicitamente alcune richieste cross-origin rifiutandone 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.

Warning

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

Ogni approccio è dettagliato nelle sezioni seguenti.

CORS con criterio denominato e middleware

Il middleware CORS gestisce le richieste tra origini diverse. 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 collocata dopo UseRouting, ma prima di UseAuthorization. Per ulteriori informazioni, vedi Ordine del middleware.
  • Chiama 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 il _myAllowSpecificOrigins criterio CORS per tutti gli endpoint dei controller. Vedere Routing degli endpoint per applicare un criterio CORS a endpoint specifici.
  • Quando si utilizza Response Caching Middleware, chiamare UseCors prima di UseResponseCaching.

Con il routing degli endpoint, il middleware CORS deve essere configurato in modo da essere eseguito 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 di UseCors e UseStaticFiles

In genere, UseStaticFiles viene chiamato prima di UseCors. Le applicazioni che usano JavaScript per recuperare file statici tra siti diversi devono chiamare UseCors prima di UseStaticFiles.

CORS con criterio predefinito e middleware

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.
  • /echo e gli endpoint del controller consentono richieste tra origini diverse usando il criterio specificato.
  • Gli endpoint delle pagine /echo2 e Razornon consentono richieste cross-origin poiché non è stato specificato alcun criterio predefinito.

L'attributo [DisableCors] nondisabilita 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. Sconsigliamo di 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] nondisabilita 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 .

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

Note

Specificare AllowAnyOrigin e AllowCredentials è una configurazione non sicura e può causare vulnerabilità di tipo cross-site request forgery. Il servizio CORS restituisce una risposta CORS non valida quando un'app è configurata con entrambi i metodi.

AllowAnyOrigin influisce sulle richieste preflight 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();

Nel codice precedente, SetIsOriginAllowedToAllowWildcardSubdomains viene chiamato con origine jolly "https://*.example.com". Questa configurazione consente le richieste CORS da qualsiasi sottodominio di example.com, ad esempio https://subdomain.example.com o https://api.example.com. Il * carattere jolly deve essere incluso nell'origine per abilitare la corrispondenza del sottodominio con caratteri jolly.

Impostare i metodi HTTP consentiti

AllowAnyMethod:

  • Consente qualsiasi metodo HTTP:
  • Interessa le richieste preflight e l'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 delle richieste di autorizzazione, chiama 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 preflight e sull'intestazione Access-Control-Request-Headers. Per altre informazioni, vedere la sezione Richieste preliminari .

Una corrispondenza della policy del middleware CORS con intestazioni specifiche specificate da WithHeaders è possibile solo quando le intestazioni inviate in Access-Control-Request-Headers corrispondono esattamente alle intestazioni specificate 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 204 No Content, ma non invia le intestazioni CORS in risposta. Pertanto, il browser non tenta la richiesta tra origini diverse.

Impostare le intestazioni di risposta esposte

Per impostazione predefinita, il browser non espone all'applicazione tutti gli header della risposta. Per ulteriori informazioni, consultare W3C Cross-Origin Resource Sharing (Terminologia): Simple Response Header.

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 intestazioni di risposta semplici. Per rendere disponibili altri header 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 origini diverse

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

Utilizzo diretto di XMLHttpRequest:

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 accettare le credenziali. Per consentire le credenziali tra origini diverse, 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 cross-origin comporta 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 relativa alle intestazioni della richiesta impostate per la richiesta del client si applica alle intestazioni che l'app imposta chiamando setRequestHeader sull'oggetto XMLHttpRequest. La specifica CORS definisce questi header header di richiesta dell'autore. La regola non si applica alle intestazioni che il browser può impostare, ad esempio User-Agent, Hosto Content-Length.

Note

Questo articolo contiene gli URL creati distribuendo il codice di esempio in due siti Web di Azure e https://cors3.azurewebsites.nethttps://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, questo non include le intestazioni che il browser imposta, ad esempio User-Agent.

Se la richiesta di preflight viene negata, l'app restituisce una risposta 204 No Content, ma non imposta le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra origini diverse. 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 origini diverse bloccata: il criterio della stessa origine non consente di leggere la risorsa remota all'indirizzo https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Motivo: richiesta CORS non riuscita). Ulteriori informazioni
  • Basato su Chromium: l'accesso per il recupero da 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' dall'origine 'https://cors3.azurewebsites.net' è stato bloccato dal criterio CORS: la risposta alla richiesta preflight non supera il controllo di accesso: l'intestazione 'Access-Control-Allow-Origin' non è presente nella risorsa richiesta. Se una risposta opaca è sufficiente, imposta la modalità della richiesta su 'no-cors' per ottenere 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 delle richieste di autorizzazione, chiama 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 qualsiasi valore diverso da "*"
  • AllowAnyHeader si chiama: includere almeno Accept, Content-Type e Origin, oltre a eventuali intestazioni personalizzate da supportare.

Codice automatico della richiesta di preflight

Quando viene applicato il criterio CORS in uno dei seguenti casi:

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

ASP.NET Core risponde alla richiesta preflight OPTIONS.

La sezione Test CORS di questo documento illustra questo comportamento.

Attributo [HttpOptions] per le richieste preflight

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 il tempo di scadenza del preflight

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 per 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 allentare il criterio della stessa origine.
    • Ad esempio, un attore malintenzionato potrebbe usare Cross-Site Scripting (XSS) contro il tuo sito ed eseguire una richiesta cross-site verso un sito sotto il suo controllo con CORS abilitato 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 da origini diverse.

La specifica CORS ha introdotto diverse nuove intestazioni HTTP che consentono richieste tra origini diverse. Se un browser supporta CORS, imposta automaticamente questi header per le richieste cross-origin. Il codice JavaScript personalizzato non è necessario per abilitare CORS.

Di seguito è riportato un esempio di richiesta tra origini diverse dal pulsante di test Values a https://cors1.azurewebsites.net/api/values. L'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 della richiesta

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 richieste OPTIONS, il server imposta l'intestazione Intestazioni di rispostaAccess-Control-Allow-Origin: {allowed origin} nella risposta. Ad esempio, nel codice di esempio, la richiesta del pulsante Delete [EnableCors]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 della richiesta

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 precedenti intestazioni di risposta, 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 viene chiamato AllowAnyOrigin, viene restituito Access-Control-Allow-Origin: *, il valore jolly. AllowAnyOrigin consente qualsiasi origine.

Se la risposta non include l'intestazione Access-Control-Allow-Origin, la richiesta cross-origin non riesce. 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 da HTTP a HTTPS causa ERR_INVALID_REDIRECT nella richiesta di preflight CORS

Le richieste a un endpoint che usano HTTP e vengono reindirizzate a HTTPS da UseHttpsRedirection non riescono 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 il codice per verificare CORS. Consulta come scaricare. L'esempio è un progetto API con Razor Pagine aggiunte:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "MyPolicy",
        policy =>
        {
            policy.WithOrigins("http://example.com",
                    "http://www.contoso.com",
                    "https://cors1.azurewebsites.net",
                    "https://cors3.azurewebsites.net",
                    "https://localhost:44398",
                    "https://localhost:5001")
                .WithMethods("PUT", "DELETE", "GET");
        });
});

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

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

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

app.Run();

Warning

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

Note

Se si usano launchSettings.json in Visual Studio o si configurano le impostazioni di debug C# in VS Code e si usa IIS Express per eseguire il debug in locale, assicurarsi di aver configurato IIS Express per "anonymousAuthentication": true. Quando "anonymousAuthentication" è false, l'host dell'ambiente Web di ASP.NET Core non vedrà alcuna richiesta preliminare. In particolare, se si usa l'autenticazione NTLM ("windowsAuthentication": true), il primo passaggio della richiesta di verifica NTLM consiste nell'inviare al Web browser una richiesta 401, che può rendere difficile verificare che la route preliminare sia configurata correttamente.

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.
  • Eseguite l'esempio in Visual Studio con la porta impostata su 44398, per un URL pari a https://localhost:44398.

Uso di un browser con gli strumenti F12:

  • Seleziona il pulsante Valori ed esamina le intestazioni nella scheda Rete.

  • Selezionare il pulsante PUT test.Select the PUT test button. Per istruzioni su come visualizzare la richiesta OPTIONS, vedere Visualizzare la richiesta 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 alla risorsa recuperata da 'https://cors1.azurewebsites.net/api/values/GetValues2' dall'origine 'https://cors3.azurewebsites.net' è stato bloccato dal criterio CORS: l'intestazione "Access-Control-Allow-Origin" richiesta non è presente nella risorsa. Se una risposta opaca è sufficiente, imposta la modalità della richiesta su 'no-cors' per ottenere 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 è cross-origin in base al valore dell'intestazione Origin:

  • Non è necessario che il middleware CORS elabori la richiesta.
  • Gli header CORS non vengono restituiti 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 RequireCors per consentire richieste tra origini diverse 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] funzionano, perché gli endpoint hanno [EnableCors] e rispondono alle richieste preflight. Gli altri endpoint hanno esito negativo. Il pulsante GET ha esito negativo perché JavaScript invia:

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

Di seguito TodoItems2Controller fornisce 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, seleziona Preflight e quindi Set 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 è chiamata criterio della stessa origine. La policy della stessa origine impedisce a un sito dannoso di leggere dati sensibili da 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 delle risorse tra origini diverse (CORS):

  • È uno standard W3C che consente a un server di allentare il criterio della stessa origine.
  • Non è una funzione di sicurezza, CORS allenta la sicurezza. Un'API non è più sicura consentendo CORS. Per altre informazioni, vedere Funzionamento di CORS.
  • Consente a un server di autorizzare esplicitamente alcune richieste cross-origin rifiutandone 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.

Warning

UseCors deve essere chiamato nell'ordine corretto. Per ulteriori 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 criterio denominato e middleware

Il middleware CORS gestisce le richieste cross-origin. 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 posizionata dopo UseRouting, ma prima di UseAuthorization. Per ulteriori informazioni, vedere Ordine del middleware.
  • Chiama 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 il _myAllowSpecificOrigins criterio CORS per tutti gli endpoint dei controller. Vedere Routing degli endpoint per applicare un criterio CORS a endpoint specifici.
  • Quando si utilizza Response Caching Middleware, chiamare UseCors prima di UseResponseCaching.

Con il routing degli endpoint, il middleware CORS deve essere configurato in modo da essere eseguito 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.

Warning

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 di UseCors e UseStaticFiles

In genere, UseStaticFiles viene chiamato prima di UseCors. Le applicazioni che usano JavaScript per recuperare file statici tra siti diversi devono chiamare UseCors prima di UseStaticFiles.

CORS con criterio predefinito e middleware

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.
  • /echo e gli endpoint del controller consentono richieste cross-origin usando il criterio specificato.
  • Gli endpoint Pages /echo2 e Razornon consentono richieste cross-origin perché non è stato specificato alcun criterio predefinito.

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

In .NET 7, l'attributo [EnableCors] deve passare un parametro o verrà generato un avviso ASP0023 a causa di una corrispondenza ambigua nel percorso. .NET 8 o versione successiva 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. Si consiglia di non combinare i criteri. Usare il [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:

  • Usa [EnableCors("MyPolicy")] con un criterio denominato.
  • Non definire una regola predefinita.
  • Non usare il routing degli endpoint.

Il codice nella sezione successiva soddisfa l'elenco precedente.

Disabilitare CORS

L'attributo [DisableCors] nondisabilita 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 autorizzate

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.

Note

Specificare AllowAnyOrigin e AllowCredentials è una configurazione non sicura e può causare attacchi di cross-site request forgery. Il servizio CORS restituisce una risposta CORS non valida quando un'app è configurata con entrambi i metodi.

AllowAnyOrigin influisce sulle richieste di preflight 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();

Nel codice precedente, SetIsOriginAllowedToAllowWildcardSubdomains viene chiamato con origine jolly "https://*.example.com". Questa configurazione consente le richieste CORS da qualsiasi sottodominio di example.com, ad esempio https://subdomain.example.com o https://api.example.com. Il * carattere jolly deve essere incluso nell'origine per abilitare la corrispondenza del sottodominio con caratteri jolly.

Impostare i metodi HTTP consentiti

AllowAnyMethod:

  • Consente qualsiasi metodo HTTP:
  • Si applica alle richieste preflight e all'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 tutti gli header di richiesta author, 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 preflight e sull'intestazione Access-Control-Request-Headers. Per altre informazioni, vedere la sezione Richieste preliminari .

Una corrispondenza della policy del middleware CORS con intestazioni specifiche specificate da WithHeaders è possibile solo quando le intestazioni inviate in Access-Control-Request-Headers corrispondono esattamente alle intestazioni specificate 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 origini diverse.

Impostare le intestazioni di risposta esposte

Per impostazione predefinita, il browser non espone all'applicazione tutti gli header della risposta. Per ulteriori informazioni, consultare W3C Cross-Origin Resource Sharing (Terminologia): Simple Response Header.

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 intestazioni di risposta semplici. Per rendere disponibili altri header 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 origini diverse

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

Utilizzo diretto di XMLHttpRequest:

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 accettare le credenziali. Per consentire le credenziali tra origini diverse, 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 cross-origin rappresenta 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 di preflight

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 relativa alle intestazioni della richiesta impostate per la richiesta del client si applica alle intestazioni che l'app imposta chiamando setRequestHeader sull'oggetto XMLHttpRequest. La specifica CORS definisce questi header header 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 di preflight viene negata, l'app restituisce una risposta 200 OK, ma non imposta le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra origini diverse. 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 origini diverse bloccata: il criterio della stessa origine non consente di leggere la risorsa remota all'indirizzo https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Motivo: richiesta CORS non riuscita). Ulteriori informazioni
  • Basato su Chromium: l'accesso per il recupero da 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' dall'origine 'https://cors3.azurewebsites.net' è stato bloccato dal criterio CORS: la risposta alla richiesta preflight non supera il controllo di accesso: l'intestazione 'Access-Control-Allow-Origin' non è presente nella risorsa richiesta. Se una risposta opaca è sufficiente, imposta la modalità della richiesta su 'no-cors' per ottenere 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 qualsiasi valore diverso da "*"
  • AllowAnyHeader si chiama: includere almeno Accept, Content-Type e Origin, oltre a eventuali intestazioni personalizzate da supportare.

Codice automatico della richiesta di preflight

Quando viene applicato il criterio CORS in uno dei seguenti casi:

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

ASP.NET Core risponde alla richiesta preflight OPTIONS.

La sezione Test CORS di questo documento illustra questo comportamento.

Attributo [HttpOptions] per le richieste preflight

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 il tempo di scadenza del preflight

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 per 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 allentare il criterio della stessa origine.
    • Ad esempio, un attore malintenzionato potrebbe usare Cross-Site Scripting (XSS) contro il tuo sito ed eseguire una richiesta cross-site verso un sito sotto il suo controllo con CORS abilitato 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 da origini diverse.

La specifica CORS ha introdotto diverse nuove intestazioni HTTP che consentono richieste tra origini diverse. Se un browser supporta CORS, imposta automaticamente questi header per le richieste cross-origin. Il codice JavaScript personalizzato non è necessario per abilitare CORS.

Selezionare il pulsante di test PUT nell'esempio distribuito. L'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 della richiesta

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 richieste OPTIONS, il server imposta l'intestazione Intestazioni di rispostaAccess-Control-Allow-Origin: {allowed origin} nella risposta. Ad esempio, nel codice di esempio, la richiesta del pulsante Delete [EnableCors]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 della richiesta

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 precedenti intestazioni di risposta, 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 viene chiamato AllowAnyOrigin, viene restituito Access-Control-Allow-Origin: *, il valore jolly. AllowAnyOrigin consente qualsiasi origine.

Se la risposta non include l'intestazione Access-Control-Allow-Origin, la richiesta cross-origin non riesce. 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 da HTTP a HTTPS causa ERR_INVALID_REDIRECT nella richiesta di preflight CORS

Le richieste a un endpoint che usano HTTP e vengono reindirizzate a HTTPS da UseHttpsRedirection non riescono 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 il codice per verificare CORS. Consulta come scaricare. L'esempio è un progetto API con Razor Pagine aggiunte:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "MyPolicy",
        policy =>
        {
            policy.WithOrigins("http://example.com",
                    "http://www.contoso.com",
                    "https://cors1.azurewebsites.net",
                    "https://cors3.azurewebsites.net",
                    "https://localhost:44398",
                    "https://localhost:5001")
                .WithMethods("PUT", "DELETE", "GET");
        });
});

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

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

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

app.Run();

Warning

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

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.
  • Eseguite l'esempio in Visual Studio con la porta impostata su 44398, per un URL pari a https://localhost:44398.

Uso di un browser con gli strumenti F12:

  • Seleziona il pulsante Valori ed esamina le intestazioni nella scheda Rete.

  • Selezionare il pulsante PUT test.Select the PUT test button. Per istruzioni su come visualizzare la richiesta OPTIONS, vedere Visualizzare la richiesta 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 alla risorsa recuperata da 'https://cors1.azurewebsites.net/api/values/GetValues2' dall'origine 'https://cors3.azurewebsites.net' è stato bloccato dal criterio CORS: l'intestazione "Access-Control-Allow-Origin" richiesta non è presente nella risorsa. Se una risposta opaca è sufficiente, imposta la modalità della richiesta su 'no-cors' per ottenere 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 è cross-origin in base al valore dell'intestazione Origin:

  • Non è necessario che il middleware CORS elabori la richiesta.
  • Gli header CORS non vengono restituiti 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 RequireCors per consentire richieste tra origini diverse 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] funzionano, perché gli endpoint hanno [EnableCors] e rispondono alle richieste preflight. Gli altri endpoint hanno esito negativo. Il pulsante GET ha esito negativo perché JavaScript invia:

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

Di seguito TodoItems2Controller fornisce 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 e quindi 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 è chiamata criterio della stessa origine. La policy della stessa origine impedisce a un sito dannoso di leggere dati sensibili da 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 delle risorse tra origini diverse (CORS):

  • È uno standard W3C che consente a un server di allentare il criterio della stessa origine.
  • Non è una funzione di sicurezza, CORS allenta la sicurezza. Un'API non è più sicura consentendo CORS. Per altre informazioni, vedere Funzionamento di CORS.
  • Consente a un server di autorizzare esplicitamente alcune richieste cross-origin rifiutandone 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.

Warning

UseCors deve essere chiamato nell'ordine corretto. Per ulteriori 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 criterio denominato e middleware

Il middleware CORS gestisce le richieste cross-origin. 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 posizionata dopo UseRouting, ma prima di UseAuthorization. Per ulteriori informazioni, vedere Ordine del middleware.
  • Chiama 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 il criterio di _myAllowSpecificOrigins CORS per tutti gli endpoint del controller. Vedere Routing degli endpoint per applicare un criterio CORS a endpoint specifici.
  • Quando si usa Response Caching Middleware, chiamare UseCors prima di UseResponseCaching.

Con il routing degli endpoint, il middleware CORS deve essere configurato per essere eseguito 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.

Warning

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 di UseCors e UseStaticFiles

In genere, UseStaticFiles viene chiamato prima di UseCors. Le applicazioni che usano JavaScript per recuperare file statici da siti diversi devono chiamare UseCors prima di UseStaticFiles.

CORS con criterio predefinito e middleware

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 singolo endpoint mediante RequireCorsnon supporta le richieste preflight automatiche. Per altre informazioni, vedere questa issue 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 endpoint /echo e del controller consentono richieste tra origini diverse utilizzando i criteri specificati.
  • Gli endpoint /echo2 e Razor Pages non consentono richieste tra origini diverse perché non è stato specificato alcun criterio predefinito.

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

Per istruzioni su come testare codice simile al precedente, vedere Test di CORS con il routing degli endpoint e [HttpOptions].

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. Si sconsiglia di 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:

  • Usa [EnableCors("MyPolicy")] con una policy con nome.
  • 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] nondisabilita 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 autorizzate

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.

Note

Specificare AllowAnyOrigin e AllowCredentials è una configurazione non sicura e può causare attacchi di cross-site request forgery. Il servizio CORS restituisce una risposta CORS non valida quando un'app è configurata con entrambi i metodi.

AllowAnyOrigin influisce sulle richieste di preflight 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();

Nel codice precedente, SetIsOriginAllowedToAllowWildcardSubdomains viene chiamato con origine jolly "https://*.example.com". Questa configurazione consente le richieste CORS da qualsiasi sottodominio di example.com, ad esempio https://subdomain.example.com o https://api.example.com. Il * carattere jolly deve essere incluso nell'origine per abilitare la corrispondenza del sottodominio con caratteri jolly.

Impostare i metodi HTTP consentiti

AllowAnyMethod:

  • Consente qualsiasi metodo HTTP:
  • Si applica alle richieste preflight e all'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 tutti gli header di richiesta di autorizzazione, 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 preflight e sull'intestazione Access-Control-Request-Headers. Per altre informazioni, vedere la sezione Richieste preliminari .

Una corrispondenza della policy del middleware CORS con intestazioni specifiche specificate da WithHeaders è possibile solo quando le intestazioni inviate in Access-Control-Request-Headers corrispondono esattamente alle intestazioni specificate 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 origini diverse.

Impostare le intestazioni di risposta esposte

Per impostazione predefinita, il browser non espone all'applicazione tutti gli header della risposta. Per ulteriori informazioni, consultare W3C Cross-Origin Resource Sharing (Terminologia): Simple Response Header.

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 intestazioni di risposta semplici. Per rendere disponibili altri header 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 origini diverse

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

Utilizzo diretto di XMLHttpRequest:

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 accettare le credenziali. Per consentire le credenziali tra origini diverse, 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 cross-origin comporta 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 relativa alle intestazioni della richiesta impostate per la richiesta del client si applica alle intestazioni che l'app imposta chiamando setRequestHeader sull'oggetto XMLHttpRequest. La specifica CORS definisce questi header header 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 di preflight viene negata, l'app restituisce una risposta 200 OK, ma non imposta le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra origini diverse. 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 origini diverse bloccata: il criterio della stessa origine non consente di leggere la risorsa remota all'indirizzo https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Motivo: richiesta CORS non riuscita). Ulteriori informazioni
  • Basato su Chromium: l'accesso per il recupero da 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' dall'origine 'https://cors3.azurewebsites.net' è stato bloccato dal criterio CORS: la risposta alla richiesta preflight non supera il controllo di accesso: l'intestazione 'Access-Control-Allow-Origin' non è presente nella risorsa richiesta. Se una risposta opaca è sufficiente, imposta la modalità della richiesta su 'no-cors' per ottenere 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 qualsiasi valore diverso da "*"
  • AllowAnyHeader si chiama: includere almeno Accept, Content-Type e Origin, oltre a eventuali intestazioni personalizzate da supportare.

Codice automatico della richiesta di preflight

Quando viene applicato il criterio CORS in uno dei seguenti casi:

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

ASP.NET Core risponde alla richiesta preflight OPTIONS.

L'abilitazione del CORS per endpoint tramite RequireCors attualmente non supporta le richieste preflight automatiche.

La sezione Test CORS di questo documento illustra questo comportamento.

Attributo [HttpOptions] per le richieste preflight

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 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 il tempo di scadenza del preflight

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 allentare il criterio della stessa origine.
    • Ad esempio, un attore malintenzionato potrebbe usare Cross-Site Scripting (XSS) contro il tuo sito ed eseguire una richiesta cross-site verso un sito sotto il suo controllo con CORS abilitato 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 da origini diverse.

La specifica CORS ha introdotto diverse nuove intestazioni HTTP che consentono richieste tra origini diverse. Se un browser supporta CORS, imposta automaticamente questi header per le richieste cross-origin. Il codice JavaScript personalizzato non è necessario per abilitare CORS.

Di seguito è riportato un esempio di richiesta tra origini diverse dal pulsante di test Values a https://cors1.azurewebsites.net/api/values. L'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 della richiesta

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 richieste OPTIONS, il server imposta l'intestazione Intestazioni di rispostaAccess-Control-Allow-Origin: {allowed origin} nella risposta. Ad esempio, l'esempio distribuito, la richiesta di pulsante OPTIONS Elimina 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 della richiesta

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 precedenti intestazioni di risposta, 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 viene chiamato AllowAnyOrigin, viene restituito Access-Control-Allow-Origin: *, il valore jolly. AllowAnyOrigin consente qualsiasi origine.

Se la risposta non include l'intestazione Access-Control-Allow-Origin, la richiesta cross-origin non riesce. 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 da HTTP a HTTPS causa ERR_INVALID_REDIRECT nella richiesta di preflight CORS

Le richieste a un endpoint che usano HTTP e vengono reindirizzate a HTTPS da UseHttpsRedirection non riescono 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.

Visualizza 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.
  • restart.

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 il codice per verificare CORS. Consulta come scaricare. L'esempio è un progetto API con Razor Pagine aggiunte:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "MyPolicy",
                policy =>
                {
                    policy.WithOrigins("http://example.com",
                        "http://www.contoso.com",
                        "https://cors1.azurewebsites.net",
                        "https://cors3.azurewebsites.net",
                        "https://localhost:44398",
                        "https://localhost:5001")
                            .WithMethods("PUT", "DELETE", "GET");
                });
});

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

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

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

app.Run();

Warning

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

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.
  • Eseguite l'esempio in Visual Studio con la porta impostata su 44398, per un URL pari a https://localhost:44398.

Uso di un browser con gli strumenti F12:

  • Seleziona il pulsante Valori ed esamina le intestazioni nella scheda Rete.

  • Selezionare il pulsante PUT test.Select the PUT test button. Per istruzioni su come visualizzare la richiesta OPTIONS, vedere Visualizzare la richiesta 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 alla risorsa recuperata da 'https://cors1.azurewebsites.net/api/values/GetValues2' dall'origine 'https://cors3.azurewebsites.net' è stato bloccato dal criterio CORS: l'intestazione "Access-Control-Allow-Origin" richiesta non è presente nella risorsa. Se una risposta opaca soddisfa le tue esigenze, imposta la modalità della richiesta su '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 è cross-origin in base al valore dell'intestazione Origin:

  • Non è necessario che il middleware CORS elabori la richiesta.
  • Gli header CORS non vengono restituiti 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

Test di CORS con il routing degli endpoint e [HttpOptions]

L'abilitazione di CORS per endpoint tramite RequireCors attualmente non supporta le richieste di preflight 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);
}

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

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

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

Di seguito TodoItems2Controller fornisce 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. Nell'elenco a discesa Controller, selezionare Preflight e quindi 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 è chiamata criterio della stessa origine. La policy della stessa origine impedisce a un sito dannoso di leggere dati sensibili da 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 delle risorse tra origini diverse (CORS):

  • È uno standard W3C che consente a un server di allentare il criterio della stessa origine.
  • Non è una funzione di sicurezza, CORS allenta la sicurezza. Un'API non è più sicura consentendo CORS. Per altre informazioni, vedere Funzionamento di CORS.
  • Consente a un server di autorizzare esplicitamente alcune richieste cross-origin rifiutandone 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.

Warning

UseCors deve essere chiamato nell'ordine corretto. Per ulteriori 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 criterio denominato e middleware

Il middleware CORS gestisce le richieste cross-origin. 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 posizionata dopo UseRouting, ma prima di UseAuthorization. Per ulteriori informazioni, vedere Ordine del middleware.
  • Chiama 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 il _myAllowSpecificOrigins criterio CORS per tutti gli endpoint dei controller. Vedere Routing degli endpoint per applicare un criterio CORS a endpoint specifici.
  • Quando si utilizza Response Caching Middleware, chiamare UseCors prima di UseResponseCaching.

Con il routing degli endpoint, il middleware CORS deve essere configurato in modo da essere eseguito 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 criterio predefinito e middleware

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 singolo endpoint mediante RequireCorsnon supporta le richieste preflight automatiche. Per altre informazioni, vedere questa issue 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.
  • /echo e gli endpoint del controller consentono richieste tra origini diverse usando il criterio specificato.
  • Gli endpoint delle pagine /echo2 e Razornon consentono richieste cross-origin poiché non è stato specificato alcun criterio predefinito.

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

Per istruzioni su come testare codice simile al precedente, vedere Test di CORS con il routing degli endpoint e [HttpOptions].

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. Si sconsiglia di 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:

  • Usa [EnableCors("MyPolicy")] con una policy con nome.
  • 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] nondisabilita 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 autorizzate

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.

Note

Specificare AllowAnyOrigin e AllowCredentials è una configurazione non sicura e può causare attacchi di cross-site request forgery. Il servizio CORS restituisce una risposta CORS non valida quando un'app è configurata con entrambi i metodi.

AllowAnyOrigin influisce sulle richieste di preflight 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();
    });

Nel codice precedente, SetIsOriginAllowedToAllowWildcardSubdomains viene chiamato con origine jolly "https://*.example.com". Questa configurazione consente le richieste CORS da qualsiasi sottodominio di example.com, ad esempio https://subdomain.example.com o https://api.example.com. Il * carattere jolly deve essere incluso nell'origine per abilitare la corrispondenza del sottodominio con caratteri jolly.

Impostare i metodi HTTP consentiti

AllowAnyMethod:

  • Consente qualsiasi metodo HTTP:
  • Si applica alle richieste preflight e all'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 preflight e sull'intestazione Access-Control-Request-Headers. Per altre informazioni, vedere la sezione Richieste preliminari .

Una corrispondenza della policy del middleware CORS con intestazioni specifiche specificate da WithHeaders è possibile solo quando le intestazioni inviate in Access-Control-Request-Headers corrispondono esattamente alle intestazioni specificate 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 origini diverse.

Impostare le intestazioni di risposta esposte

Per impostazione predefinita, il browser non espone all'applicazione tutti gli header della risposta. Per ulteriori informazioni, consultare W3C Cross-Origin Resource Sharing (Terminologia): Simple Response Header.

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 intestazioni di risposta semplici. Per rendere disponibili altri header all’app, chiamare WithExposedHeaders:

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

Credenziali nelle richieste tra origini diverse

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

Utilizzo diretto di XMLHttpRequest:

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 accettare le credenziali. Per consentire le credenziali tra origini diverse, 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 cross-origin comporta 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 relativa alle intestazioni della richiesta impostate per la richiesta del client si applica alle intestazioni che l'app imposta chiamando setRequestHeader sull'oggetto XMLHttpRequest. La specifica CORS definisce questi header header 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 di preflight viene negata, l'app restituisce una risposta 200 OK, ma non imposta le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra origini diverse. 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 origini diverse bloccata: il criterio della stessa origine non consente di leggere la risorsa remota all'indirizzo https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Motivo: richiesta CORS non riuscita). Ulteriori informazioni
  • Basato su Chromium: l'accesso per il recupero da 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' dall'origine 'https://cors3.azurewebsites.net' è stato bloccato dal criterio CORS: la risposta alla richiesta preflight non supera il controllo di accesso: l'intestazione 'Access-Control-Allow-Origin' non è presente nella risorsa richiesta. Se una risposta opaca è sufficiente, imposta la modalità della richiesta su 'no-cors' per ottenere 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 qualsiasi valore diverso da "*"
  • AllowAnyHeader si chiama: includere almeno Accept, Content-Type e Origin, oltre a eventuali intestazioni personalizzate da supportare.

Codice automatico della richiesta di preflight

Quando viene applicato il criterio CORS in uno dei seguenti casi:

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

ASP.NET Core risponde alla richiesta preflight OPTIONS.

L'abilitazione del CORS per endpoint tramite RequireCors attualmente non supporta le richieste preflight automatiche.

La sezione Test CORS di questo documento illustra questo comportamento.

Attributo [HttpOptions] per le richieste preflight

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 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 il tempo di scadenza del preflight

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 allentare il criterio della stessa origine.
    • Ad esempio, un attore malintenzionato potrebbe usare Cross-Site Scripting (XSS) contro il tuo sito ed eseguire una richiesta cross-site verso un sito sotto il suo controllo con CORS abilitato 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 da origini diverse.

La specifica CORS ha introdotto diverse nuove intestazioni HTTP che consentono richieste tra origini diverse. Se un browser supporta CORS, imposta automaticamente questi header per le richieste cross-origin. Il codice JavaScript personalizzato non è necessario per abilitare CORS.

Di seguito è riportato un esempio di richiesta tra origini diverse dal pulsante di test Values a https://cors1.azurewebsites.net/api/values. L'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 della richiesta

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 richieste OPTIONS, il server imposta l'intestazione Intestazioni di rispostaAccess-Control-Allow-Origin: {allowed origin} nella risposta. Ad esempio, l'esempio distribuito, la richiesta di pulsante OPTIONS Elimina 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 della richiesta

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 precedenti intestazioni di risposta, 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 viene chiamato AllowAnyOrigin, viene restituito Access-Control-Allow-Origin: *, il valore jolly. AllowAnyOrigin consente qualsiasi origine.

Se la risposta non include l'intestazione Access-Control-Allow-Origin, la richiesta cross-origin non riesce. 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.

Visualizza 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.
  • restart.

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 il codice per verificare CORS. Consulta come scaricare. L'esempio è un progetto API con Razor Pagine aggiunte:

public class StartupTest2
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: "MyPolicy",
                policy =>
                {
                    policy.WithOrigins("http://example.com",
                        "http://www.contoso.com",
                        "https://cors1.azurewebsites.net",
                        "https://cors3.azurewebsites.net",
                        "https://localhost:44398",
                        "https://localhost:5001")
                            .WithMethods("PUT", "DELETE", "GET");
                });
        });

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

    public void Configure(IApplicationBuilder app)
    {
        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

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

Warning

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

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.
  • Eseguite l'esempio in Visual Studio con la porta impostata su 44398, per un URL pari a https://localhost:44398.

Uso di un browser con gli strumenti F12:

  • Seleziona il pulsante Valori ed esamina le intestazioni nella scheda Rete.

  • Selezionare il pulsante PUT test.Select the PUT test button. Per istruzioni su come visualizzare la richiesta OPTIONS, vedere Visualizzare la richiesta 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 alla risorsa recuperata da 'https://cors1.azurewebsites.net/api/values/GetValues2' dall'origine 'https://cors3.azurewebsites.net' è stato bloccato dal criterio CORS: l'intestazione "Access-Control-Allow-Origin" richiesta non è presente nella risorsa. Se una risposta opaca è sufficiente, imposta la modalità della richiesta su 'no-cors' per ottenere 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 è cross-origin in base al valore dell'intestazione Origin:

  • Non è necessario che il middleware CORS elabori la richiesta.
  • Gli header CORS non vengono restituiti 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

Test di CORS con il routing degli endpoint e [HttpOptions]

L'abilitazione di CORS per endpoint tramite RequireCors attualmente non supporta le richieste di preflight 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);
}

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

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

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

Di seguito TodoItems2Controller fornisce 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. Nell'elenco a discesa Controller, selezionare Preflight e quindi Imposta controller. Tutte le chiamate CORS agli TodoItems2Controller endpoint hanno esito positivo.

Risorse aggiuntive