Abilitare le richieste tra le origini (CORS) in ASP.NET Core
Nota
Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 9 di questo articolo.
Avviso
Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere Criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 8 di questo articolo.
Importante
Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.
Per la versione corrente, vedere la versione .NET 9 di questo articolo.
Di Rick Anderson e Kirk Larkin
Questo articolo illustra in che modo Cross-O rigin Resource Sharing (CORS) è abilitato in un'app ASP.NET Core.
La sicurezza del browser impedisce a una pagina Web di effettuare richieste a un dominio diverso da quello che ha distribuito la pagina Web. Questa restrizione è nota come criteri di corrispondenza dell'origine. I criteri di corrispondenza dell'origine impediscono a un sito dannoso di accedere a dati sensibili in un altro sito. In alcuni casi, potrebbe essere necessario consentire ad altri siti di effettuare richieste tra origini all'app. Per altre informazioni, vedere l'articolo Mozilla CORS.
Condivisione di risorse tra le origini (CORS):
- È uno standard W3C che consente a un server di rilassare i criteri di stessa origine.
- Non è una funzionalità di sicurezza, CORS riduce la sicurezza. Un'API non è più sicura consentendo CORS. Per altre informazioni, vedere Funzionamento di CORS.
- Consente a un server di consentire in modo esplicito alcune richieste tra le origini rifiutando altre.
- È più sicuro e più flessibile rispetto alle tecniche precedenti, ad esempio JSONP.
Visualizzare o scaricare il codice di esempio (procedura per il download)
Stessa origine
Due URL hanno la stessa origine se hanno schemi, host e porte identici (RFC 6454).
Questi due URL hanno la stessa origine:
https://example.com/foo.html
https://example.com/bar.html
Questi URL hanno origini diverse rispetto ai due URL precedenti:
https://example.net
: dominio diversohttps://contoso.example.com/foo.html
: sottodominio diversohttp://example.com/foo.html
: schema diversohttps://example.com:9000/foo.html
: porta diversa
Abilitare CORS
Esistono tre modi per abilitare CORS:
- Nel middleware usando un criterio denominato o un criterio predefinito.
- Uso del routing degli endpoint.
- Con l'attributo [EnableCors].
L'uso dell'attributo [EnableCors] con un criterio denominato consente di controllare meglio gli endpoint che supportano CORS.
Avviso
UseCors deve essere chiamato nell'ordine corretto. Per altre informazioni, vedere Ordine del middleware. Ad esempio, UseCors
deve essere chiamato prima UseResponseCaching di quando si usa UseResponseCaching
.
Ogni approccio è dettagliato nelle sezioni seguenti.
CORS con criteri e middleware denominati
Il middleware CORS gestisce le richieste tra le origini. Il codice seguente applica un criterio CORS a tutti gli endpoint dell'app con le origini specificate:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
Il codice precedente:
- Imposta il nome del criterio su
_myAllowSpecificOrigins
. Il nome del criterio è arbitrario. - Chiama il UseCors metodo di estensione e specifica i
_myAllowSpecificOrigins
criteri CORS.UseCors
aggiunge il middleware CORS. La chiamata aUseCors
deve essere inserita dopoUseRouting
, ma primaUseAuthorization
di . Per altre informazioni, vedere Ordine del middleware. - Chiamate AddCors con un'espressione lambda. L'espressione lambda accetta un CorsPolicyBuilder oggetto . Le opzioni di configurazione, ad esempio
WithOrigins
, sono descritte più avanti in questo articolo. - Abilita i
_myAllowSpecificOrigins
criteri CORS per tutti gli endpoint controller. Vedere Routing degli endpoint per applicare un criterio CORS a endpoint specifici. - Quando si usa il middleware di memorizzazione nella cache delle risposte, chiamare UseCors prima UseResponseCachingdi .
Con il routing degli endpoint, il middleware CORS deve essere configurato per l'esecuzione tra le chiamate a UseRouting
e UseEndpoints
.
La AddCors chiamata al metodo aggiunge servizi CORS al contenitore del servizio dell'app:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
Per altre informazioni, vedere Opzioni dei criteri CORS in questo documento.
I CorsPolicyBuilder metodi possono essere concatenati, come illustrato nel codice seguente:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
Nota: l'URL specificato non deve contenere una barra finale (/
). Se l'URL termina con /
, il confronto restituisce false
e non viene restituita alcuna intestazione.
Ordine UseCors e UseStaticFiles
In genere, UseStaticFiles
viene chiamato prima UseCors
di . Le app che usano JavaScript per recuperare i file statici tra siti devono chiamare UseCors
prima UseStaticFiles
di .
CORS con criteri e middleware predefiniti
Il codice evidenziato seguente abilita i criteri CORS predefiniti:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.Run();
Il codice precedente applica i criteri CORS predefiniti a tutti gli endpoint controller.
Abilitare Cors con il routing degli endpoint
Con il routing degli endpoint, CORS può essere abilitato per ogni endpoint usando il RequireCors set di metodi di estensione:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/echo",
context => context.Response.WriteAsync("echo"))
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapControllers()
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapGet("/echo2",
context => context.Response.WriteAsync("echo2"));
endpoints.MapRazorPages();
});
app.Run();
Nel codice precedente:
app.UseCors
abilita il middleware CORS. Poiché non è stato configurato un criterio predefinito,app.UseCors()
da solo non abilita CORS.- Gli
/echo
endpoint del controller e consentono le richieste tra le origini usando i criteri specificati. - Gli
/echo2
endpoint e Razor Pages non consentono le richieste tra le origini perché non è stato specificato alcun criterio predefinito.
L'attributo [DisableCors] non disabilita CORS abilitato dal routing degli endpoint con RequireCors
.
Vedere Testare CORS con l'attributo [EnableCors] e il metodo RequireCors per istruzioni sul test del codice simile al precedente.
Abilitare CORS con attributi
L'abilitazione di CORS con l'attributo [EnableCors] e l'applicazione di un criterio denominato solo agli endpoint che richiedono CORS fornisce il controllo migliore.
L'attributo [EnableCors] offre un'alternativa all'applicazione globale di CORS. L'attributo [EnableCors]
abilita CORS per gli endpoint selezionati, anziché per tutti gli endpoint:
[EnableCors]
specifica il criterio predefinito.[EnableCors("{Policy String}")]
specifica un criterio denominato.
L'attributo [EnableCors]
può essere applicato a:
- Razor Pagina
PageModel
- Controller
- Metodo di azione del controller
È possibile applicare criteri diversi ai controller, ai modelli di pagina o ai metodi di azione con l'attributo [EnableCors]
. Quando l'attributo [EnableCors]
viene applicato a un controller, a un modello di pagina o a un metodo di azione e CORS è abilitato nel middleware, vengono applicati entrambi i criteri. È consigliabile combinare i criteri. Usare[EnableCors]
attributo o middleware, non entrambi nella stessa app.
Il codice seguente applica criteri diversi a ogni metodo:
[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
// GET api/values
[EnableCors("AnotherPolicy")]
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "green widget", "red widget" };
}
// GET api/values/5
[EnableCors("Policy1")]
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return id switch
{
1 => "green widget",
2 => "red widget",
_ => NotFound(),
};
}
}
Il codice seguente crea due criteri CORS:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("Policy1",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
options.AddPolicy("AnotherPolicy",
policy =>
{
policy.WithOrigins("http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.Run();
Per il controllo più dettagliato della limitazione delle richieste CORS:
- Usare
[EnableCors("MyPolicy")]
con un criterio denominato. - Non definire un criterio predefinito.
- Non usare il routing degli endpoint.
Il codice nella sezione successiva soddisfa l'elenco precedente.
Disabilitare CORS
L'attributo [DisableCors] non disabilita CORS abilitato dal routing degli endpoint.
Il codice seguente definisce i criteri CORS "MyPolicy"
:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapControllers();
endpoints.MapRazorPages();
});
app.Run();
Il codice seguente disabilita CORS per l'azione GetValues2
:
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
Il codice precedente:
- Non abilita CORS con il routing degli endpoint.
- Non definisce un criterio CORS predefinito.
- Usa [EnableCors("MyPolicy")] per abilitare i
"MyPolicy"
criteri CORS per il controller. - Disabilita CORS per il
GetValues2
metodo .
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:
- Impostare le origini consentite
- Impostare i metodi HTTP consentiti
- Impostare le intestazioni di richiesta consentite
- Impostare le intestazioni di risposta esposte
- Credenziali nelle richieste tra le origini
- Impostare l'ora di scadenza preliminare
AddPolicy viene chiamato in Program.cs
. Per alcune opzioni, può essere utile leggere prima la sezione Funzionamento di CORS .
Impostare le origini consentite
AllowAnyOrigin: consente le richieste CORS da tutte le origini con qualsiasi schema (http
o https
). AllowAnyOrigin
non è sicuro perché qualsiasi sito Web può effettuare richieste tra le origini all'app.
Nota
Specificare AllowAnyOrigin
e AllowCredentials
è una configurazione non sicura e può comportare la falsificazione della richiesta intersito. Il servizio CORS restituisce una risposta CORS non valida quando un'app è configurata con entrambi i metodi.
AllowAnyOrigin
influisce sulle richieste preliminari e sull'intestazione Access-Control-Allow-Origin
. Per altre informazioni, vedere la sezione Richieste preliminari .
SetIsOriginAllowedToAllowWildcardSubdomains: imposta la IsOriginAllowed proprietà del criterio come funzione che consente alle origini di trovare una corrispondenza con un dominio con caratteri jolly configurato durante la valutazione se l'origine è consentita.
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
});
builder.Services.AddControllers();
var app = builder.Build();
Impostare i metodi HTTP consentiti
- Consente qualsiasi metodo HTTP:
- Influisce sulle richieste preliminari e sull'intestazione
Access-Control-Allow-Methods
. Per altre informazioni, vedere la sezione Richieste preliminari .
Impostare le intestazioni di richiesta consentite
Per consentire l'invio di intestazioni specifiche in una richiesta CORS, denominata intestazioni di richiesta autore, chiamare WithHeaders e specificare le intestazioni consentite:
using Microsoft.Net.Http.Headers;
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Per consentire tutte le intestazioni di richiesta dell'autore, chiamare AllowAnyHeader:
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
AllowAnyHeader
influisce sulle richieste preliminari e sull'intestazione Access-Control-Request-Headers . Per altre informazioni, vedere la sezione Richieste preliminari .
Un criterio middleware CORS corrisponde a intestazioni specifiche specificate da WithHeaders
è possibile solo quando le intestazioni inviate in Access-Control-Request-Headers
corrispondono esattamente alle intestazioni indicate in WithHeaders
.
Si consideri, ad esempio, un'app configurata come segue:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
Il middleware CORS rifiuta una richiesta preliminare con l'intestazione di richiesta seguente perché Content-Language
(HeaderNames.ContentLanguage) non è elencato in WithHeaders
:
Access-Control-Request-Headers: Cache-Control, Content-Language
L'app restituisce una risposta 200 OK , ma non invia di nuovo le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra le origini.
Impostare le intestazioni di risposta esposte
Per impostazione predefinita, il browser non espone tutte le intestazioni di risposta all'app. Per altre informazioni, vedere W3C Cross-Origin Resource Sharing (Terminologia): Intestazione di risposta semplice.
Le intestazioni di risposta disponibili per impostazione predefinita sono:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
La specifica CORS chiama queste intestazioni semplici intestazioni di risposta. Per rendere disponibili altre intestazioni all'app, chiamare WithExposedHeaders:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyExposeResponseHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.WithExposedHeaders("x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Credenziali nelle richieste tra le origini
Le credenziali richiedono una gestione speciale in una richiesta CORS. Per impostazione predefinita, il browser non invia le credenziali con una richiesta tra le origini. Le credenziali includono cookie e schemi di autenticazione HTTP. Per inviare le credenziali con una richiesta tra le origini, il client deve impostare su XMLHttpRequest.withCredentials
true
.
Uso XMLHttpRequest
diretto:
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
Uso di jQuery:
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
Il server deve consentire le credenziali. Per consentire le credenziali tra le origini, chiamare AllowCredentials:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyMyAllowCredentialsPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.AllowCredentials();
});
});
builder.Services.AddControllers();
var app = builder.Build();
La risposta HTTP include un'intestazione Access-Control-Allow-Credentials
che indica al browser che il server consente le credenziali per una richiesta tra le origini.
Se il browser invia credenziali ma la risposta non include un'intestazione valida Access-Control-Allow-Credentials
, il browser non espone la risposta all'app e la richiesta tra le origini ha esito negativo.
Consentire le credenziali tra le origini è un rischio per la sicurezza. Un sito Web in un altro dominio può inviare le credenziali di un utente connesso all'app per conto dell'utente senza conoscere l'utente.
La specifica CORS indica inoltre che l'impostazione delle origini su "*"
(tutte le origini) non è valida se l'intestazione Access-Control-Allow-Credentials
è presente.
Richieste preliminari
Per alcune richieste CORS, il browser invia una richiesta OPTIONS aggiuntiva prima di effettuare la richiesta effettiva. Questa richiesta viene chiamata richiesta preliminare. Il browser può ignorare la richiesta preliminare se sono soddisfatte tutte le condizioni seguenti:
- Il metodo di richiesta è GET, HEAD o POST.
- L'app non imposta intestazioni di richiesta diverse da
Accept
,Accept-Language
,Content-Language
Content-Type
, oLast-Event-ID
. - L'intestazione
Content-Type
, se impostata, ha uno dei valori seguenti:application/x-www-form-urlencoded
multipart/form-data
text/plain
La regola sulle intestazioni di richiesta impostata per la richiesta client si applica alle intestazioni impostate dall'app chiamando setRequestHeader
sull'oggetto XMLHttpRequest
. La specifica CORS chiama queste intestazioni le intestazioni di richiesta dell'autore. La regola non si applica alle intestazioni che il browser può impostare, ad esempio User-Agent
, Host
o Content-Length
.
Nota
Questo articolo contiene gli URL creati distribuendo il codice di esempio in due siti Web di Azure e https://cors3.azurewebsites.net
https://cors.azurewebsites.net
.
Di seguito è riportata una risposta di esempio simile alla richiesta preliminare effettuata dal pulsante [Put test] nella sezione Test CORS di questo documento.
General:
Request URL: https://cors3.azurewebsites.net/api/values/5
Request Method: OPTIONS
Status Code: 204 No Content
Response Headers:
Access-Control-Allow-Methods: PUT,DELETE,GET
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f8...8;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Vary: Origin
Request Headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Method: PUT
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
La richiesta preliminare usa il metodo HTTP OPTIONS . Può includere le intestazioni seguenti:
- Access-Control-Request-Method: metodo HTTP che verrà usato per la richiesta effettiva.
- Access-Control-Request-Headers: elenco di intestazioni di richiesta impostate dall'app nella richiesta effettiva. Come indicato in precedenza, non include intestazioni impostate dal browser, ad esempio
User-Agent
.
Se la richiesta preliminare viene negata, l'app restituisce una 200 OK
risposta ma non imposta le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra le origini. Per un esempio di richiesta preliminare negata, vedere la sezione Test CORS di questo documento.
Usando gli strumenti F12, l'app console mostra un errore simile a uno dei seguenti, a seconda del browser:
- Firefox: Richiesta tra le origini bloccate: lo stesso criterio di origine non consente la lettura della risorsa remota in
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Motivo: richiesta CORS non riuscita). Altre informazioni - Basato su Chromium: l'accesso al recupero all'origine 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' dall'origine 'https://cors3.azurewebsites.net' è stato bloccato dai criteri CORS: la risposta alla richiesta preliminare non supera il controllo di accesso: nessuna intestazione 'Access-Control-Allow-Origin' è presente nella risorsa richiesta. Se si ritiene soddisfacente una risposta opaca, impostare la modalità della richiesta 'no-cors' per recuperare la risorsa con CORS disabilitato.
Per consentire intestazioni specifiche, chiamare WithHeaders:
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Per consentire tutte le intestazioni di richiesta dell'autore, chiamare AllowAnyHeader:
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
I browser non sono coerenti nel modo in cui impostano Access-Control-Request-Headers
. In caso affermativo:
- Le intestazioni sono impostate su un valore diverso da
"*"
- AllowAnyHeader viene chiamato: includere almeno
Accept
,Content-Type
eOrigin
, più eventuali intestazioni personalizzate che si desidera supportare.
Codice di richiesta preliminare automatico
Quando vengono applicati i criteri CORS:
- A livello globale chiamando
app.UseCors
inProgram.cs
. - Uso dell'attributo
[EnableCors]
.
ASP.NET Core risponde alla richiesta OPTIONS preliminare.
La sezione Test CORS di questo documento illustra questo comportamento.
Attributo [HttpOptions] per le richieste preliminari
Quando CORS è abilitato con i criteri appropriati, ASP.NET Core risponde in genere alle richieste preliminari CORS automaticamente.
Il codice seguente usa l'attributo [HttpOptions] per creare endpoint per le richieste OPTIONS:
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
Vedere Testare CORS con l'attributo [EnableCors] e il metodo RequireCors per istruzioni sul test del codice precedente.
Impostare l'ora di scadenza preliminare
L'intestazione Access-Control-Max-Age
specifica per quanto tempo è possibile memorizzare nella cache la risposta alla richiesta preliminare. Per impostare questa intestazione, chiamare SetPreflightMaxAge:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MySetPreflightExpirationPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
});
});
builder.Services.AddControllers();
var app = builder.Build();
Abilitare CORS in un endpoint
Funzionamento di CORS
Questa sezione descrive cosa accade in una richiesta CORS a livello di messaggi HTTP.
- CORS non è una funzionalità di sicurezza. CORS è uno standard W3C che consente a un server di ridurre i criteri di stessa origine.
- Ad esempio, un attore malintenzionato potrebbe usare xss (Cross-Site Scripting) sul sito ed eseguire una richiesta tra siti al sito abilitato per CORS per rubare informazioni.
- Un'API non è più sicura consentendo CORS.
- Spetta al client (browser) applicare CORS. Il server esegue la richiesta e restituisce la risposta, è il client che restituisce un errore e blocca la risposta. Ad esempio, uno degli strumenti seguenti visualizzerà la risposta del server:
- Fiddler
- .NET HttpClient
- Un Web browser immettendo l'URL nella barra degli indirizzi.
- Spetta al client (browser) applicare CORS. Il server esegue la richiesta e restituisce la risposta, è il client che restituisce un errore e blocca la risposta. Ad esempio, uno degli strumenti seguenti visualizzerà la risposta del server:
- È un modo per consentire ai browser di eseguire una richiesta XHR o Fetch API di origine incrociata che altrimenti sarebbe vietata.
- I browser senza CORS non possono eseguire richieste tra le origini. Prima di CORS, JSONP veniva usato per aggirare questa restrizione. JSONP non usa XHR, ma usa il
<script>
tag per ricevere la risposta. Gli script possono essere caricati tra le origini.
- 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
La specifica CORS ha introdotto diverse nuove intestazioni HTTP che abilitano le richieste tra le origini. Se un browser supporta CORS, imposta automaticamente queste intestazioni per le richieste tra le origini. Il codice JavaScript personalizzato non è necessario per abilitare CORS.
Di seguito è riportato un esempio di richiesta tra le origini dal pulsante di test Values a https://cors1.azurewebsites.net/api/values
. Intestazione Origin
:
- Fornisce il dominio del sito che effettua la richiesta.
- È obbligatorio e deve essere diverso dall'host.
Intestazioni generali
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
Intestazioni di risposta
Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET
Intestazioni delle richieste
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...
Nelle OPTIONS
richieste, il server imposta l'intestazione Intestazioni Access-Control-Allow-Origin: {allowed origin}
risposta nella risposta. Nel codice di esempio, ad esempio, la Delete [EnableCors]
richiesta del pulsante OPTIONS
contiene le intestazioni seguenti:
Intestazioni generali
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
Intestazioni di risposta
Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET
Intestazioni delle richieste
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
Nelle intestazioni response precedenti il server imposta l'intestazione Access-Control-Allow-Origin nella risposta. Il https://cors1.azurewebsites.net
valore di questa intestazione corrisponde all'intestazione Origin
della richiesta.
Se AllowAnyOrigin viene chiamato , Access-Control-Allow-Origin: *
viene restituito il valore con caratteri jolly . AllowAnyOrigin
consente qualsiasi origine.
Se la risposta non include l'intestazione Access-Control-Allow-Origin
, la richiesta tra le origini ha esito negativo. In particolare, il browser non consente la richiesta. Anche se il server restituisce una risposta corretta, il browser non rende disponibile la risposta all'app client.
Il reindirizzamento HTTP a HTTPS causa ERR_INVALID_REDIRECT nella richiesta preliminare CORS
Le richieste a un endpoint che usano HTTP reindirizzate a HTTPS UseHttpsRedirection hanno esito negativo con ERR_INVALID_REDIRECT on the CORS preflight request
.
I progetti API possono rifiutare le richieste HTTP anziché usarle UseHttpsRedirection
per reindirizzare le richieste a HTTPS.
CORS in IIS
Quando si esegue la distribuzione in IIS, CORS deve essere eseguito prima dell'autenticazione di Windows se il server non è configurato per consentire l'accesso anonimo. Per supportare questo scenario, è necessario installare e configurare il modulo CORS IIS per l'app.
Testare CORS
Il download di esempio include codice per testare CORS. Vedere come scaricare un esempio. L'esempio è un progetto API con Razor Pages aggiunto:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.MapRazorPages();
app.Run();
Avviso
WithOrigins("https://localhost:<port>");
deve essere usato solo per testare un'app di esempio simile al codice di esempio di download.
Di seguito ValuesController
vengono forniti gli endpoint per il test:
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
MyDisplayRouteInfo viene fornito dal pacchetto NuGet Rick.Docs.Samples.RouteInfo e visualizza le informazioni sulla route.
Testare il codice di esempio precedente usando uno degli approcci seguenti:
- Eseguire l'esempio con
dotnet run
usando l'URL predefinito dihttps://localhost:5001
. - Eseguire l'esempio da Visual Studio con la porta impostata su 44398 per un URL di
https://localhost:44398
.
Uso di un browser con gli strumenti F12:
Selezionare il pulsante Valori ed esaminare le intestazioni nella scheda Rete .
Selezionare il pulsante PUT test.Select the PUT test button. Per istruzioni sulla visualizzazione della richiesta OPTIONS, vedere Visualizzare le richieste OPTIONS. Il test PUT crea due richieste, una richiesta preliminare OPTIONS e la richiesta PUT.
Selezionare il
GetValues2 [DisableCors]
pulsante per attivare una richiesta CORS non riuscita. Come accennato nel documento, la risposta restituisce 200 risultati positivi, ma la richiesta CORS non viene effettuata. Selezionare la scheda Console per visualizzare l'errore CORS. A seconda del browser, viene visualizzato un errore simile al seguente:L'accesso al recupero dall'origine
'https://cors1.azurewebsites.net/api/values/GetValues2'
'https://cors3.azurewebsites.net'
è stato bloccato dai criteri CORS: nessuna intestazione "Access-Control-Allow-Origin" è presente nella risorsa richiesta. Se si ritiene soddisfacente una risposta opaca, impostare la modalità della richiesta 'no-cors' per recuperare la risorsa con CORS disabilitato.
Gli endpoint abilitati per CORS possono essere testati con uno strumento, ad esempio curl o Fiddler. Quando si usa uno strumento, l'origine della richiesta specificata dall'intestazione Origin
deve essere diversa dall'host che riceve la richiesta. Se la richiesta non è tra le origini in base al valore dell'intestazione Origin
:
- Non è necessario che il middleware CORS elabori la richiesta.
- Le intestazioni CORS non vengono restituite nella risposta.
Il comando seguente usa curl
per emettere una richiesta OPTIONS con informazioni:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
Testare CORS con l'attributo [EnableCors] e il metodo RequireCors
Si consideri il codice seguente che usa il routing degli endpoint per abilitare CORS per ogni endpoint usando RequireCors
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/echo",
context => context.Response.WriteAsync("echo"))
.RequireCors("MyPolicy");
endpoints.MapControllers();
endpoints.MapRazorPages();
});
app.Run();
Si noti che solo l'endpoint /echo
usa per RequireCors
consentire le richieste tra le origini usando i criteri specificati. I controller seguenti abilitano CORS usando l'attributo [EnableCors].
Di seguito TodoItems1Controller
sono riportati gli endpoint per il test:
[Route("api/[controller]")]
[ApiController]
public class TodoItems1Controller : ControllerBase
{
// PUT: api/TodoItems1/5
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id) {
if (id < 1) {
return Content($"ID = {id}");
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// Delete: api/TodoItems1/5
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/TodoItems1
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors("MyPolicy")]
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
// Delete: api/TodoItems1/MyDelete2/5
[EnableCors("MyPolicy")]
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
I pulsanti Delete [EnableCors] e GET [EnableCors] hanno esito positivo, perché gli endpoint hanno [EnableCors]
e rispondono alle richieste preliminari. Gli altri endpoint hanno esito negativo. Il pulsante GET ha esito negativo perché JavaScript invia:
headers: {
"Content-Type": "x-custom-header"
},
Di seguito TodoItems2Controller
sono disponibili endpoint simili, ma include codice esplicito per rispondere alle richieste OPTIONS:
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// [EnableCors] // Not needed as OPTIONS path provided.
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// [EnableCors] // Warning ASP0023 Route '{id}' conflicts with another action route.
// An HTTP request that matches multiple routes results in an ambiguous
// match error.
[EnableCors("MyPolicy")] // Required for this path.
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors("MyPolicy")] // Required for this path.
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
Il codice precedente può essere testato distribuendo l'esempio in Azure. Nell'elenco a discesa Controller selezionare Preflight (Preflight ) e quindi Set Controller (Imposta controller). Tutte le chiamate CORS agli TodoItems2Controller
endpoint hanno esito positivo.
Risorse aggiuntive
Di Rick Anderson e Kirk Larkin
Questo articolo illustra come abilitare CORS in un'app core ASP.NET.
La sicurezza del browser impedisce a una pagina Web di effettuare richieste a un dominio diverso da quello che ha distribuito la pagina Web. Questa restrizione è nota come criteri di corrispondenza dell'origine. I criteri di corrispondenza dell'origine impediscono a un sito dannoso di accedere a dati sensibili in un altro sito. In alcuni casi, potrebbe essere necessario consentire ad altri siti di effettuare richieste tra origini all'app. Per altre informazioni, vedere l'articolo Mozilla CORS.
Condivisione di risorse tra le origini (CORS):
- È uno standard W3C che consente a un server di rilassare i criteri di stessa origine.
- Non è una funzionalità di sicurezza, CORS riduce la sicurezza. Un'API non è più sicura consentendo CORS. Per altre informazioni, vedere Funzionamento di CORS.
- Consente a un server di consentire in modo esplicito alcune richieste tra le origini rifiutando altre.
- È più sicuro e più flessibile rispetto alle tecniche precedenti, ad esempio JSONP.
Visualizzare o scaricare il codice di esempio (procedura per il download)
Stessa origine
Due URL hanno la stessa origine se hanno schemi, host e porte identici (RFC 6454).
Questi due URL hanno la stessa origine:
https://example.com/foo.html
https://example.com/bar.html
Questi URL hanno origini diverse rispetto ai due URL precedenti:
https://example.net
: dominio diversohttps://www.example.com/foo.html
: sottodominio diversohttp://example.com/foo.html
: schema diversohttps://example.com:9000/foo.html
: porta diversa
Abilitare CORS
Esistono tre modi per abilitare CORS:
- Nel middleware usando un criterio denominato o un criterio predefinito.
- Uso del routing degli endpoint.
- Con l'attributo [EnableCors].
L'uso dell'attributo [EnableCors] con un criterio denominato consente di controllare meglio gli endpoint che supportano CORS.
Avviso
UseCors deve essere chiamato nell'ordine corretto. Per altre informazioni, vedere Ordine del middleware. Ad esempio, UseCors
deve essere chiamato prima UseResponseCaching di quando si usa UseResponseCaching
.
Ogni approccio è dettagliato nelle sezioni seguenti.
CORS con criteri e middleware denominati
Il middleware CORS gestisce le richieste tra le origini. Il codice seguente applica un criterio CORS a tutti gli endpoint dell'app con le origini specificate:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
Il codice precedente:
- Imposta il nome del criterio su
_myAllowSpecificOrigins
. Il nome del criterio è arbitrario. - Chiama il UseCors metodo di estensione e specifica i
_myAllowSpecificOrigins
criteri CORS.UseCors
aggiunge il middleware CORS. La chiamata aUseCors
deve essere inserita dopoUseRouting
, ma primaUseAuthorization
di . Per altre informazioni, vedere Ordine del middleware. - Chiamate AddCors con un'espressione lambda. L'espressione lambda accetta un CorsPolicyBuilder oggetto . Le opzioni di configurazione, ad esempio
WithOrigins
, sono descritte più avanti in questo articolo. - Abilita i
_myAllowSpecificOrigins
criteri CORS per tutti gli endpoint controller. Vedere Routing degli endpoint per applicare un criterio CORS a endpoint specifici. - Quando si usa il middleware di memorizzazione nella cache delle risposte, chiamare UseCors prima UseResponseCachingdi .
Con il routing degli endpoint, il middleware CORS deve essere configurato per l'esecuzione tra le chiamate a UseRouting
e UseEndpoints
.
La AddCors chiamata al metodo aggiunge servizi CORS al contenitore del servizio dell'app:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
Per altre informazioni, vedere Opzioni dei criteri CORS in questo documento.
I CorsPolicyBuilder metodi possono essere concatenati, come illustrato nel codice seguente:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
Nota: l'URL specificato non deve contenere una barra finale (/
). Se l'URL termina con /
, il confronto restituisce false
e non viene restituita alcuna intestazione.
Avviso
UseCors
deve essere posizionato dopo UseRouting
e prima UseAuthorization
di . Ciò consente di assicurarsi che le intestazioni CORS siano incluse nella risposta sia per le chiamate autorizzate che non autorizzate.
Ordine UseCors e UseStaticFiles
In genere, UseStaticFiles
viene chiamato prima UseCors
di . Le app che usano JavaScript per recuperare i file statici tra siti devono chiamare UseCors
prima UseStaticFiles
di .
CORS con criteri e middleware predefiniti
Il codice evidenziato seguente abilita i criteri CORS predefiniti:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.Run();
Il codice precedente applica i criteri CORS predefiniti a tutti gli endpoint controller.
Abilitare Cors con il routing degli endpoint
Con il routing degli endpoint, CORS può essere abilitato per ogni endpoint usando il RequireCors set di metodi di estensione:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/echo",
context => context.Response.WriteAsync("echo"))
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapControllers()
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapGet("/echo2",
context => context.Response.WriteAsync("echo2"));
endpoints.MapRazorPages();
});
app.Run();
Nel codice precedente:
app.UseCors
abilita il middleware CORS. Poiché non è stato configurato un criterio predefinito,app.UseCors()
da solo non abilita CORS.- Gli
/echo
endpoint del controller e consentono le richieste tra le origini usando i criteri specificati. - Gli
/echo2
endpoint e Razor Pages non consentono le richieste tra le origini perché non è stato specificato alcun criterio predefinito.
L'attributo [DisableCors] non disabilita CORS abilitato dal routing degli endpoint con RequireCors
.
In ASP.NET Core 7.0, l'attributo [EnableCors]
deve passare un parametro o un avviso ASP0023 viene generato da una corrispondenza ambigua nella route. ASP.NET Core 8.0 e versioni successive non genera l'avviso ASP0023
.
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// [EnableCors] // Not needed as OPTIONS path provided.
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// [EnableCors] // Warning ASP0023 Route '{id}' conflicts with another action route.
// An HTTP request that matches multiple routes results in an ambiguous
// match error.
[EnableCors("MyPolicy")] // Required for this path.
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors("MyPolicy")] // Required for this path.
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
Vedere Testare CORS con l'attributo [EnableCors] e il metodo RequireCors per istruzioni sul test del codice simile al precedente.
Abilitare CORS con attributi
L'abilitazione di CORS con l'attributo [EnableCors] e l'applicazione di un criterio denominato solo agli endpoint che richiedono CORS fornisce il controllo migliore.
L'attributo [EnableCors] offre un'alternativa all'applicazione globale di CORS. L'attributo [EnableCors]
abilita CORS per gli endpoint selezionati, anziché per tutti gli endpoint:
[EnableCors]
specifica il criterio predefinito.[EnableCors("{Policy String}")]
specifica un criterio denominato.
L'attributo [EnableCors]
può essere applicato a:
- Razor Pagina
PageModel
- Controller
- Metodo di azione del controller
È possibile applicare criteri diversi ai controller, ai modelli di pagina o ai metodi di azione con l'attributo [EnableCors]
. Quando l'attributo [EnableCors]
viene applicato a un controller, a un modello di pagina o a un metodo di azione e CORS è abilitato nel middleware, vengono applicati entrambi i criteri. È consigliabile combinare i criteri. Usare[EnableCors]
attributo o middleware, non entrambi nella stessa app.
Il codice seguente applica criteri diversi a ogni metodo:
[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
// GET api/values
[EnableCors("AnotherPolicy")]
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "green widget", "red widget" };
}
// GET api/values/5
[EnableCors("Policy1")]
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return id switch
{
1 => "green widget",
2 => "red widget",
_ => NotFound(),
};
}
}
Il codice seguente crea due criteri CORS:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("Policy1",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
options.AddPolicy("AnotherPolicy",
policy =>
{
policy.WithOrigins("http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.Run();
Per il controllo più dettagliato della limitazione delle richieste CORS:
- Usare
[EnableCors("MyPolicy")]
con un criterio denominato. - Non definire un criterio predefinito.
- Non usare il routing degli endpoint.
Il codice nella sezione successiva soddisfa l'elenco precedente.
Disabilitare CORS
L'attributo [DisableCors] non disabilita CORS abilitato dal routing degli endpoint.
Il codice seguente definisce i criteri CORS "MyPolicy"
:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapControllers();
endpoints.MapRazorPages();
});
app.Run();
Il codice seguente disabilita CORS per l'azione GetValues2
:
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
Il codice precedente:
- Non abilita CORS con il routing degli endpoint.
- Non definisce un criterio CORS predefinito.
- Usa [EnableCors("MyPolicy")] per abilitare i
"MyPolicy"
criteri CORS per il controller. - Disabilita CORS per il
GetValues2
metodo .
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:
- Impostare le origini consentite
- Impostare i metodi HTTP consentiti
- Impostare le intestazioni di richiesta consentite
- Impostare le intestazioni di risposta esposte
- Credenziali nelle richieste tra le origini
- Impostare l'ora di scadenza preliminare
AddPolicy viene chiamato in Program.cs
. Per alcune opzioni, può essere utile leggere prima la sezione Funzionamento di CORS .
Impostare le origini consentite
AllowAnyOrigin: consente le richieste CORS da tutte le origini con qualsiasi schema (http
o https
). AllowAnyOrigin
non è sicuro perché qualsiasi sito Web può effettuare richieste tra le origini all'app.
Nota
Specificare AllowAnyOrigin
e AllowCredentials
è una configurazione non sicura e può comportare la falsificazione della richiesta intersito. Il servizio CORS restituisce una risposta CORS non valida quando un'app è configurata con entrambi i metodi.
AllowAnyOrigin
influisce sulle richieste preliminari e sull'intestazione Access-Control-Allow-Origin
. Per altre informazioni, vedere la sezione Richieste preliminari .
SetIsOriginAllowedToAllowWildcardSubdomains: imposta la IsOriginAllowed proprietà del criterio come funzione che consente alle origini di trovare una corrispondenza con un dominio con caratteri jolly configurato durante la valutazione se l'origine è consentita.
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
});
builder.Services.AddControllers();
var app = builder.Build();
Impostare i metodi HTTP consentiti
- Consente qualsiasi metodo HTTP:
- Influisce sulle richieste preliminari e sull'intestazione
Access-Control-Allow-Methods
. Per altre informazioni, vedere la sezione Richieste preliminari .
Impostare le intestazioni di richiesta consentite
Per consentire l'invio di intestazioni specifiche in una richiesta CORS, denominata intestazioni di richiesta autore, chiamare WithHeaders e specificare le intestazioni consentite:
using Microsoft.Net.Http.Headers;
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Per consentire tutte le intestazioni di richiesta dell'autore, chiamare AllowAnyHeader:
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
AllowAnyHeader
influisce sulle richieste preliminari e sull'intestazione Access-Control-Request-Headers . Per altre informazioni, vedere la sezione Richieste preliminari .
Un criterio middleware CORS corrisponde a intestazioni specifiche specificate da WithHeaders
è possibile solo quando le intestazioni inviate in Access-Control-Request-Headers
corrispondono esattamente alle intestazioni indicate in WithHeaders
.
Si consideri, ad esempio, un'app configurata come segue:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
Il middleware CORS rifiuta una richiesta preliminare con l'intestazione di richiesta seguente perché Content-Language
(HeaderNames.ContentLanguage) non è elencato in WithHeaders
:
Access-Control-Request-Headers: Cache-Control, Content-Language
L'app restituisce una risposta 200 OK , ma non invia di nuovo le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra le origini.
Impostare le intestazioni di risposta esposte
Per impostazione predefinita, il browser non espone tutte le intestazioni di risposta all'app. Per altre informazioni, vedere W3C Cross-Origin Resource Sharing (Terminologia): Intestazione di risposta semplice.
Le intestazioni di risposta disponibili per impostazione predefinita sono:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
La specifica CORS chiama queste intestazioni semplici intestazioni di risposta. Per rendere disponibili altre intestazioni all'app, chiamare WithExposedHeaders:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyExposeResponseHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.WithExposedHeaders("x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Credenziali nelle richieste tra le origini
Le credenziali richiedono una gestione speciale in una richiesta CORS. Per impostazione predefinita, il browser non invia le credenziali con una richiesta tra le origini. Le credenziali includono cookie e schemi di autenticazione HTTP. Per inviare le credenziali con una richiesta tra le origini, il client deve impostare su XMLHttpRequest.withCredentials
true
.
Uso XMLHttpRequest
diretto:
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
Uso di jQuery:
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
Il server deve consentire le credenziali. Per consentire le credenziali tra le origini, chiamare AllowCredentials:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyMyAllowCredentialsPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.AllowCredentials();
});
});
builder.Services.AddControllers();
var app = builder.Build();
La risposta HTTP include un'intestazione Access-Control-Allow-Credentials
che indica al browser che il server consente le credenziali per una richiesta tra le origini.
Se il browser invia credenziali ma la risposta non include un'intestazione valida Access-Control-Allow-Credentials
, il browser non espone la risposta all'app e la richiesta tra le origini ha esito negativo.
Consentire le credenziali tra le origini è un rischio per la sicurezza. Un sito Web in un altro dominio può inviare le credenziali di un utente connesso all'app per conto dell'utente senza conoscere l'utente.
La specifica CORS indica inoltre che l'impostazione delle origini su "*"
(tutte le origini) non è valida se l'intestazione Access-Control-Allow-Credentials
è presente.
Richieste preliminari
Per alcune richieste CORS, il browser invia una richiesta OPTIONS aggiuntiva prima di effettuare la richiesta effettiva. Questa richiesta viene chiamata richiesta preliminare. Il browser può ignorare la richiesta preliminare se sono soddisfatte tutte le condizioni seguenti:
- Il metodo di richiesta è GET, HEAD o POST.
- L'app non imposta intestazioni di richiesta diverse da
Accept
,Accept-Language
,Content-Language
Content-Type
, oLast-Event-ID
. - L'intestazione
Content-Type
, se impostata, ha uno dei valori seguenti:application/x-www-form-urlencoded
multipart/form-data
text/plain
La regola sulle intestazioni di richiesta impostata per la richiesta client si applica alle intestazioni impostate dall'app chiamando setRequestHeader
sull'oggetto XMLHttpRequest
. La specifica CORS chiama queste intestazioni le intestazioni di richiesta dell'autore. La regola non si applica alle intestazioni che il browser può impostare, ad esempio User-Agent
, Host
o 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:
- Access-Control-Request-Method: metodo HTTP che verrà usato per la richiesta effettiva.
- Access-Control-Request-Headers: elenco di intestazioni di richiesta impostate dall'app nella richiesta effettiva. Come indicato in precedenza, non include intestazioni impostate dal browser, ad esempio
User-Agent
. - Access-Control-Allow-Methods
Se la richiesta preliminare viene negata, l'app restituisce una 200 OK
risposta ma non imposta le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra le origini. Per un esempio di richiesta preliminare negata, vedere la sezione Test CORS di questo documento.
Usando gli strumenti F12, l'app console mostra un errore simile a uno dei seguenti, a seconda del browser:
- Firefox: Richiesta tra le origini bloccate: lo stesso criterio di origine non consente la lettura della risorsa remota in
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Motivo: richiesta CORS non riuscita). Altre informazioni - Basato su Chromium: l'accesso al recupero all'origine 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' dall'origine 'https://cors3.azurewebsites.net' è stato bloccato dai criteri CORS: la risposta alla richiesta preliminare non supera il controllo di accesso: nessuna intestazione 'Access-Control-Allow-Origin' è presente nella risorsa richiesta. Se si ritiene soddisfacente una risposta opaca, impostare la modalità della richiesta 'no-cors' per recuperare la risorsa con CORS disabilitato.
Per consentire intestazioni specifiche, chiamare WithHeaders:
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Per consentire tutte le intestazioni di richiesta dell'autore, chiamare AllowAnyHeader:
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
I browser non sono coerenti nel modo in cui impostano Access-Control-Request-Headers
. In caso affermativo:
- Le intestazioni sono impostate su un valore diverso da
"*"
- AllowAnyHeader viene chiamato: includere almeno
Accept
,Content-Type
eOrigin
, più eventuali intestazioni personalizzate che si desidera supportare.
Codice di richiesta preliminare automatico
Quando vengono applicati i criteri CORS:
- A livello globale chiamando
app.UseCors
inProgram.cs
. - Uso dell'attributo
[EnableCors]
.
ASP.NET Core risponde alla richiesta OPTIONS preliminare.
La sezione Test CORS di questo documento illustra questo comportamento.
Attributo [HttpOptions] per le richieste preliminari
Quando CORS è abilitato con i criteri appropriati, ASP.NET Core risponde in genere alle richieste preliminari CORS automaticamente.
Il codice seguente usa l'attributo [HttpOptions] per creare endpoint per le richieste OPTIONS:
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
Vedere Testare CORS con l'attributo [EnableCors] e il metodo RequireCors per istruzioni sul test del codice precedente.
Impostare l'ora di scadenza preliminare
L'intestazione Access-Control-Max-Age
specifica per quanto tempo è possibile memorizzare nella cache la risposta alla richiesta preliminare. Per impostare questa intestazione, chiamare SetPreflightMaxAge:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MySetPreflightExpirationPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
});
});
builder.Services.AddControllers();
var app = builder.Build();
Abilitare CORS in un endpoint
Funzionamento di CORS
Questa sezione descrive cosa accade in una richiesta CORS a livello di messaggi HTTP.
- CORS non è una funzionalità di sicurezza. CORS è uno standard W3C che consente a un server di ridurre i criteri di stessa origine.
- Ad esempio, un attore malintenzionato potrebbe usare xss (Cross-Site Scripting) sul sito ed eseguire una richiesta tra siti al sito abilitato per CORS per rubare informazioni.
- Un'API non è più sicura consentendo CORS.
- Spetta al client (browser) applicare CORS. Il server esegue la richiesta e restituisce la risposta, è il client che restituisce un errore e blocca la risposta. Ad esempio, uno degli strumenti seguenti visualizzerà la risposta del server:
- Fiddler
- .NET HttpClient
- Un Web browser immettendo l'URL nella barra degli indirizzi.
- Spetta al client (browser) applicare CORS. Il server esegue la richiesta e restituisce la risposta, è il client che restituisce un errore e blocca la risposta. Ad esempio, uno degli strumenti seguenti visualizzerà la risposta del server:
- È un modo per consentire ai browser di eseguire una richiesta XHR o Fetch API di origine incrociata che altrimenti sarebbe vietata.
- I browser senza CORS non possono eseguire richieste tra le origini. Prima di CORS, JSONP veniva usato per aggirare questa restrizione. JSONP non usa XHR, ma usa il
<script>
tag per ricevere la risposta. Gli script possono essere caricati tra le origini.
- 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
La specifica CORS ha introdotto diverse nuove intestazioni HTTP che abilitano le richieste tra le origini. Se un browser supporta CORS, imposta automaticamente queste intestazioni per le richieste tra le origini. Il codice JavaScript personalizzato non è necessario per abilitare CORS.
Selezionare il pulsante PUT test nell'esempio distribuito.
Intestazione Origin
:
- Fornisce il dominio del sito che effettua la richiesta.
- È obbligatorio e deve essere diverso dall'host.
Intestazioni generali
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
Intestazioni di risposta
Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET
Intestazioni delle richieste
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...
Nelle OPTIONS
richieste, il server imposta l'intestazione Intestazioni Access-Control-Allow-Origin: {allowed origin}
risposta nella risposta. Nel codice di esempio, ad esempio, la Delete [EnableCors]
richiesta del pulsante OPTIONS
contiene le intestazioni seguenti:
Intestazioni generali
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
Intestazioni di risposta
Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET
Intestazioni delle richieste
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
Nelle intestazioni response precedenti il server imposta l'intestazione Access-Control-Allow-Origin nella risposta. Il https://cors1.azurewebsites.net
valore di questa intestazione corrisponde all'intestazione Origin
della richiesta.
Se AllowAnyOrigin viene chiamato , Access-Control-Allow-Origin: *
viene restituito il valore con caratteri jolly . AllowAnyOrigin
consente qualsiasi origine.
Se la risposta non include l'intestazione Access-Control-Allow-Origin
, la richiesta tra le origini ha esito negativo. In particolare, il browser non consente la richiesta. Anche se il server restituisce una risposta corretta, il browser non rende disponibile la risposta all'app client.
Il reindirizzamento HTTP a HTTPS causa ERR_INVALID_REDIRECT nella richiesta preliminare CORS
Le richieste a un endpoint che usano HTTP reindirizzate a HTTPS UseHttpsRedirection hanno esito negativo con ERR_INVALID_REDIRECT on the CORS preflight request
.
I progetti API possono rifiutare le richieste HTTP anziché usarle UseHttpsRedirection
per reindirizzare le richieste a HTTPS.
CORS in IIS
Quando si esegue la distribuzione in IIS, CORS deve essere eseguito prima dell'autenticazione di Windows se il server non è configurato per consentire l'accesso anonimo. Per supportare questo scenario, è necessario installare e configurare il modulo CORS IIS per l'app.
Testare CORS
Il download di esempio include codice per testare CORS. Vedere come scaricare un esempio. L'esempio è un progetto API con Razor Pages aggiunto:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.MapRazorPages();
app.Run();
Avviso
WithOrigins("https://localhost:<port>");
deve essere usato solo per testare un'app di esempio simile al codice di esempio di download.
Di seguito ValuesController
vengono forniti gli endpoint per il test:
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
MyDisplayRouteInfo viene fornito dal pacchetto NuGet Rick.Docs.Samples.RouteInfo e visualizza le informazioni sulla route.
Testare il codice di esempio precedente usando uno degli approcci seguenti:
- Eseguire l'esempio con
dotnet run
usando l'URL predefinito dihttps://localhost:5001
. - Eseguire l'esempio da Visual Studio con la porta impostata su 44398 per un URL di
https://localhost:44398
.
Uso di un browser con gli strumenti F12:
Selezionare il pulsante Valori ed esaminare le intestazioni nella scheda Rete .
Selezionare il pulsante PUT test.Select the PUT test button. Per istruzioni sulla visualizzazione della richiesta OPTIONS, vedere Visualizzare le richieste OPTIONS. Il test PUT crea due richieste, una richiesta preliminare OPTIONS e la richiesta PUT.
Selezionare il
GetValues2 [DisableCors]
pulsante per attivare una richiesta CORS non riuscita. Come accennato nel documento, la risposta restituisce 200 risultati positivi, ma la richiesta CORS non viene effettuata. Selezionare la scheda Console per visualizzare l'errore CORS. A seconda del browser, viene visualizzato un errore simile al seguente:L'accesso al recupero dall'origine
'https://cors1.azurewebsites.net/api/values/GetValues2'
'https://cors3.azurewebsites.net'
è stato bloccato dai criteri CORS: nessuna intestazione "Access-Control-Allow-Origin" è presente nella risorsa richiesta. Se si ritiene soddisfacente una risposta opaca, impostare la modalità della richiesta 'no-cors' per recuperare la risorsa con CORS disabilitato.
Gli endpoint abilitati per CORS possono essere testati con uno strumento, ad esempio curl o Fiddler. Quando si usa uno strumento, l'origine della richiesta specificata dall'intestazione Origin
deve essere diversa dall'host che riceve la richiesta. Se la richiesta non è tra le origini in base al valore dell'intestazione Origin
:
- Non è necessario che il middleware CORS elabori la richiesta.
- Le intestazioni CORS non vengono restituite nella risposta.
Il comando seguente usa curl
per emettere una richiesta OPTIONS con informazioni:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
Testare CORS con l'attributo [EnableCors] e il metodo RequireCors
Si consideri il codice seguente che usa il routing degli endpoint per abilitare CORS per ogni endpoint usando RequireCors
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/echo",
context => context.Response.WriteAsync("echo"))
.RequireCors("MyPolicy");
endpoints.MapControllers();
endpoints.MapRazorPages();
});
app.Run();
Si noti che solo l'endpoint /echo
usa per RequireCors
consentire le richieste tra le origini usando i criteri specificati. I controller seguenti abilitano CORS usando l'attributo [EnableCors].
Di seguito TodoItems1Controller
sono riportati gli endpoint per il test:
[Route("api/[controller]")]
[ApiController]
public class TodoItems1Controller : ControllerBase
{
// PUT: api/TodoItems1/5
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id) {
if (id < 1) {
return Content($"ID = {id}");
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// Delete: api/TodoItems1/5
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/TodoItems1
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors("MyPolicy")]
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
// Delete: api/TodoItems1/MyDelete2/5
[EnableCors("MyPolicy")]
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
I pulsanti Delete [EnableCors] e GET [EnableCors] hanno esito positivo, perché gli endpoint hanno [EnableCors]
e rispondono alle richieste preliminari. Gli altri endpoint hanno esito negativo. Il pulsante GET ha esito negativo perché JavaScript invia:
headers: {
"Content-Type": "x-custom-header"
},
Di seguito TodoItems2Controller
sono disponibili endpoint simili, ma include codice esplicito per rispondere alle richieste OPTIONS:
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// [EnableCors] // Not needed as OPTIONS path provided.
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// [EnableCors] // Warning ASP0023 Route '{id}' conflicts with another action route.
// An HTTP request that matches multiple routes results in an ambiguous
// match error.
[EnableCors("MyPolicy")] // Required for this path.
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors("MyPolicy")] // Required for this path.
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
Il codice precedente può essere testato distribuendo l'esempio in Azure.In'elenco a discesa Controller, selezionare Preflight (Verifica preliminare) e quindi Set Controller (Imposta controller). Tutte le chiamate CORS agli TodoItems2Controller
endpoint hanno esito positivo.
Risorse aggiuntive
Di Rick Anderson e Kirk Larkin
Questo articolo illustra come abilitare CORS in un'app core ASP.NET.
La sicurezza del browser impedisce a una pagina Web di effettuare richieste a un dominio diverso da quello che ha distribuito la pagina Web. Questa restrizione è nota come criteri di corrispondenza dell'origine. I criteri di corrispondenza dell'origine impediscono a un sito dannoso di accedere a dati sensibili in un altro sito. In alcuni casi, potrebbe essere necessario consentire ad altri siti di effettuare richieste tra origini all'app. Per altre informazioni, vedere l'articolo Mozilla CORS.
Condivisione di risorse tra le origini (CORS):
- È uno standard W3C che consente a un server di rilassare i criteri di stessa origine.
- Non è una funzionalità di sicurezza, CORS riduce la sicurezza. Un'API non è più sicura consentendo CORS. Per altre informazioni, vedere Funzionamento di CORS.
- Consente a un server di consentire in modo esplicito alcune richieste tra le origini rifiutando altre.
- È più sicuro e più flessibile rispetto alle tecniche precedenti, ad esempio JSONP.
Visualizzare o scaricare il codice di esempio (procedura per il download)
Stessa origine
Due URL hanno la stessa origine se hanno schemi, host e porte identici (RFC 6454).
Questi due URL hanno la stessa origine:
https://example.com/foo.html
https://example.com/bar.html
Questi URL hanno origini diverse rispetto ai due URL precedenti:
https://example.net
: dominio diversohttps://www.example.com/foo.html
: sottodominio diversohttp://example.com/foo.html
: schema diversohttps://example.com:9000/foo.html
: porta diversa
Abilitare CORS
Esistono tre modi per abilitare CORS:
- Nel middleware usando un criterio denominato o un criterio predefinito.
- Uso del routing degli endpoint.
- Con l'attributo [EnableCors].
L'uso dell'attributo [EnableCors] con un criterio denominato consente di controllare meglio gli endpoint che supportano CORS.
Avviso
UseCors deve essere chiamato nell'ordine corretto. Per altre informazioni, vedere Ordine del middleware. Ad esempio, UseCors
deve essere chiamato prima UseResponseCaching di quando si usa UseResponseCaching
.
Ogni approccio è dettagliato nelle sezioni seguenti.
CORS con criteri e middleware denominati
Il middleware CORS gestisce le richieste tra le origini. Il codice seguente applica un criterio CORS a tutti gli endpoint dell'app con le origini specificate:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
Il codice precedente:
- Imposta il nome del criterio su
_myAllowSpecificOrigins
. Il nome del criterio è arbitrario. - Chiama il UseCors metodo di estensione e specifica i
_myAllowSpecificOrigins
criteri CORS.UseCors
aggiunge il middleware CORS. La chiamata aUseCors
deve essere inserita dopoUseRouting
, ma primaUseAuthorization
di . Per altre informazioni, vedere Ordine del middleware. - Chiamate AddCors con un'espressione lambda. L'espressione lambda accetta un CorsPolicyBuilder oggetto . Le opzioni di configurazione, ad esempio
WithOrigins
, sono descritte più avanti in questo articolo. - Abilita i
_myAllowSpecificOrigins
criteri CORS per tutti gli endpoint controller. Vedere Routing degli endpoint per applicare un criterio CORS a endpoint specifici. - Quando si usa il middleware di memorizzazione nella cache delle risposte, chiamare UseCors prima UseResponseCachingdi .
Con il routing degli endpoint, il middleware CORS deve essere configurato per l'esecuzione tra le chiamate a UseRouting
e UseEndpoints
.
La AddCors chiamata al metodo aggiunge servizi CORS al contenitore del servizio dell'app:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
Per altre informazioni, vedere Opzioni dei criteri CORS in questo documento.
I CorsPolicyBuilder metodi possono essere concatenati, come illustrato nel codice seguente:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
Nota: l'URL specificato non deve contenere una barra finale (/
). Se l'URL termina con /
, il confronto restituisce false
e non viene restituita alcuna intestazione.
Avviso
UseCors
deve essere posizionato dopo UseRouting
e prima UseAuthorization
di . Ciò consente di assicurarsi che le intestazioni CORS siano incluse nella risposta sia per le chiamate autorizzate che non autorizzate.
Ordine UseCors e UseStaticFiles
In genere, UseStaticFiles
viene chiamato prima UseCors
di . Le app che usano JavaScript per recuperare i file statici tra siti devono chiamare UseCors
prima UseStaticFiles
di .
CORS con criteri e middleware predefiniti
Il codice evidenziato seguente abilita i criteri CORS predefiniti:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.Run();
Il codice precedente applica i criteri CORS predefiniti a tutti gli endpoint controller.
Abilitare Cors con il routing degli endpoint
L'abilitazione di CORS per endpoint tramite RequireCors
non supporta le richieste preliminari automatiche. Per altre informazioni, vedere questo problema di GitHub e Testare CORS con il routing degli endpoint e [HttpOptions].
Con il routing degli endpoint, CORS può essere abilitato per ogni endpoint usando il RequireCors set di metodi di estensione:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/echo",
context => context.Response.WriteAsync("echo"))
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapControllers()
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapGet("/echo2",
context => context.Response.WriteAsync("echo2"));
endpoints.MapRazorPages();
});
app.Run();
Nel codice precedente:
app.UseCors
abilita il middleware CORS. Poiché non è stato configurato un criterio predefinito,app.UseCors()
da solo non abilita CORS.- Gli
/echo
endpoint del controller e consentono le richieste tra le origini usando i criteri specificati. - Gli
/echo2
endpoint e Razor Pages non consentono le richieste tra le origini perché non è stato specificato alcun criterio predefinito.
L'attributo [DisableCors] non disabilita CORS abilitato dal routing degli endpoint con RequireCors
.
Vedere Testare CORS con il routing degli endpoint e [HttpOptions] per istruzioni sul test del codice simile al precedente.
Abilitare CORS con attributi
L'abilitazione di CORS con l'attributo [EnableCors] e l'applicazione di un criterio denominato solo agli endpoint che richiedono CORS fornisce il controllo migliore.
L'attributo [EnableCors] offre un'alternativa all'applicazione globale di CORS. L'attributo [EnableCors]
abilita CORS per gli endpoint selezionati, anziché per tutti gli endpoint:
[EnableCors]
specifica il criterio predefinito.[EnableCors("{Policy String}")]
specifica un criterio denominato.
L'attributo [EnableCors]
può essere applicato a:
- Razor Pagina
PageModel
- Controller
- Metodo di azione del controller
È possibile applicare criteri diversi ai controller, ai modelli di pagina o ai metodi di azione con l'attributo [EnableCors]
. Quando l'attributo [EnableCors]
viene applicato a un controller, a un modello di pagina o a un metodo di azione e CORS è abilitato nel middleware, vengono applicati entrambi i criteri. È consigliabile combinare i criteri. Usare[EnableCors]
attributo o middleware, non entrambi nella stessa app.
Il codice seguente applica criteri diversi a ogni metodo:
[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
// GET api/values
[EnableCors("AnotherPolicy")]
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "green widget", "red widget" };
}
// GET api/values/5
[EnableCors("Policy1")]
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return id switch
{
1 => "green widget",
2 => "red widget",
_ => NotFound(),
};
}
}
Il codice seguente crea due criteri CORS:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("Policy1",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
options.AddPolicy("AnotherPolicy",
policy =>
{
policy.WithOrigins("http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.Run();
Per il controllo più dettagliato della limitazione delle richieste CORS:
- Usare
[EnableCors("MyPolicy")]
con un criterio denominato. - Non definire un criterio predefinito.
- Non usare il routing degli endpoint.
Il codice nella sezione successiva soddisfa l'elenco precedente.
Disabilitare CORS
L'attributo [DisableCors] non disabilita CORS abilitato dal routing degli endpoint.
Il codice seguente definisce i criteri CORS "MyPolicy"
:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.MapRazorPages();
app.Run();
Il codice seguente disabilita CORS per l'azione GetValues2
:
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
Il codice precedente:
- Non abilita CORS con il routing degli endpoint.
- Non definisce un criterio CORS predefinito.
- Usa [EnableCors("MyPolicy")] per abilitare i
"MyPolicy"
criteri CORS per il controller. - Disabilita CORS per il
GetValues2
metodo .
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:
- Impostare le origini consentite
- Impostare i metodi HTTP consentiti
- Impostare le intestazioni di richiesta consentite
- Impostare le intestazioni di risposta esposte
- Credenziali nelle richieste tra le origini
- Impostare l'ora di scadenza preliminare
AddPolicy viene chiamato in Program.cs
. Per alcune opzioni, può essere utile leggere prima la sezione Funzionamento di CORS .
Impostare le origini consentite
AllowAnyOrigin: consente le richieste CORS da tutte le origini con qualsiasi schema (http
o https
). AllowAnyOrigin
non è sicuro perché qualsiasi sito Web può effettuare richieste tra le origini all'app.
Nota
Specificare AllowAnyOrigin
e AllowCredentials
è una configurazione non sicura e può comportare la falsificazione della richiesta intersito. Il servizio CORS restituisce una risposta CORS non valida quando un'app è configurata con entrambi i metodi.
AllowAnyOrigin
influisce sulle richieste preliminari e sull'intestazione Access-Control-Allow-Origin
. Per altre informazioni, vedere la sezione Richieste preliminari .
SetIsOriginAllowedToAllowWildcardSubdomains: imposta la IsOriginAllowed proprietà del criterio come funzione che consente alle origini di trovare una corrispondenza con un dominio con caratteri jolly configurato durante la valutazione se l'origine è consentita.
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
});
builder.Services.AddControllers();
var app = builder.Build();
Impostare i metodi HTTP consentiti
- Consente qualsiasi metodo HTTP:
- Influisce sulle richieste preliminari e sull'intestazione
Access-Control-Allow-Methods
. Per altre informazioni, vedere la sezione Richieste preliminari .
Impostare le intestazioni di richiesta consentite
Per consentire l'invio di intestazioni specifiche in una richiesta CORS, denominata intestazioni di richiesta autore, chiamare WithHeaders e specificare le intestazioni consentite:
using Microsoft.Net.Http.Headers;
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Per consentire tutte le intestazioni di richiesta dell'autore, chiamare AllowAnyHeader:
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
AllowAnyHeader
influisce sulle richieste preliminari e sull'intestazione Access-Control-Request-Headers . Per altre informazioni, vedere la sezione Richieste preliminari .
Un criterio middleware CORS corrisponde a intestazioni specifiche specificate da WithHeaders
è possibile solo quando le intestazioni inviate in Access-Control-Request-Headers
corrispondono esattamente alle intestazioni indicate in WithHeaders
.
Si consideri, ad esempio, un'app configurata come segue:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
Il middleware CORS rifiuta una richiesta preliminare con l'intestazione di richiesta seguente perché Content-Language
(HeaderNames.ContentLanguage) non è elencato in WithHeaders
:
Access-Control-Request-Headers: Cache-Control, Content-Language
L'app restituisce una risposta 200 OK , ma non invia di nuovo le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra le origini.
Impostare le intestazioni di risposta esposte
Per impostazione predefinita, il browser non espone tutte le intestazioni di risposta all'app. Per altre informazioni, vedere W3C Cross-Origin Resource Sharing (Terminologia): Intestazione di risposta semplice.
Le intestazioni di risposta disponibili per impostazione predefinita sono:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
La specifica CORS chiama queste intestazioni semplici intestazioni di risposta. Per rendere disponibili altre intestazioni all'app, chiamare WithExposedHeaders:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyExposeResponseHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.WithExposedHeaders("x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Credenziali nelle richieste tra le origini
Le credenziali richiedono una gestione speciale in una richiesta CORS. Per impostazione predefinita, il browser non invia le credenziali con una richiesta tra le origini. Le credenziali includono cookie e schemi di autenticazione HTTP. Per inviare le credenziali con una richiesta tra le origini, il client deve impostare su XMLHttpRequest.withCredentials
true
.
Uso XMLHttpRequest
diretto:
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
Uso di jQuery:
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
Il server deve consentire le credenziali. Per consentire le credenziali tra le origini, chiamare AllowCredentials:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyMyAllowCredentialsPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.AllowCredentials();
});
});
builder.Services.AddControllers();
var app = builder.Build();
La risposta HTTP include un'intestazione Access-Control-Allow-Credentials
che indica al browser che il server consente le credenziali per una richiesta tra le origini.
Se il browser invia credenziali ma la risposta non include un'intestazione valida Access-Control-Allow-Credentials
, il browser non espone la risposta all'app e la richiesta tra le origini ha esito negativo.
Consentire le credenziali tra le origini è un rischio per la sicurezza. Un sito Web in un altro dominio può inviare le credenziali di un utente connesso all'app per conto dell'utente senza conoscere l'utente.
La specifica CORS indica inoltre che l'impostazione delle origini su "*"
(tutte le origini) non è valida se l'intestazione Access-Control-Allow-Credentials
è presente.
Richieste preliminari
Per alcune richieste CORS, il browser invia una richiesta OPTIONS aggiuntiva prima di effettuare la richiesta effettiva. Questa richiesta viene chiamata richiesta preliminare. Il browser può ignorare la richiesta preliminare se sono soddisfatte tutte le condizioni seguenti:
- Il metodo di richiesta è GET, HEAD o POST.
- L'app non imposta intestazioni di richiesta diverse da
Accept
,Accept-Language
,Content-Language
Content-Type
, oLast-Event-ID
. - L'intestazione
Content-Type
, se impostata, ha uno dei valori seguenti:application/x-www-form-urlencoded
multipart/form-data
text/plain
La regola sulle intestazioni di richiesta impostata per la richiesta client si applica alle intestazioni impostate dall'app chiamando setRequestHeader
sull'oggetto XMLHttpRequest
. La specifica CORS chiama queste intestazioni le intestazioni di richiesta dell'autore. La regola non si applica alle intestazioni che il browser può impostare, ad esempio User-Agent
, Host
o 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:
- Access-Control-Request-Method: metodo HTTP che verrà usato per la richiesta effettiva.
- Access-Control-Request-Headers: elenco di intestazioni di richiesta impostate dall'app nella richiesta effettiva. Come indicato in precedenza, non include intestazioni impostate dal browser, ad esempio
User-Agent
. - Access-Control-Allow-Methods
Se la richiesta preliminare viene negata, l'app restituisce una 200 OK
risposta ma non imposta le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra le origini. Per un esempio di richiesta preliminare negata, vedere la sezione Test CORS di questo documento.
Usando gli strumenti F12, l'app console mostra un errore simile a uno dei seguenti, a seconda del browser:
- Firefox: Richiesta tra le origini bloccate: lo stesso criterio di origine non consente la lettura della risorsa remota in
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Motivo: richiesta CORS non riuscita). Altre informazioni - Basato su Chromium: l'accesso al recupero all'origine 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' dall'origine 'https://cors3.azurewebsites.net' è stato bloccato dai criteri CORS: la risposta alla richiesta preliminare non supera il controllo di accesso: nessuna intestazione 'Access-Control-Allow-Origin' è presente nella risorsa richiesta. Se si ritiene soddisfacente una risposta opaca, impostare la modalità della richiesta 'no-cors' per recuperare la risorsa con CORS disabilitato.
Per consentire intestazioni specifiche, chiamare WithHeaders:
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Per consentire tutte le intestazioni di richiesta dell'autore, chiamare AllowAnyHeader:
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
I browser non sono coerenti nel modo in cui impostano Access-Control-Request-Headers
. In caso affermativo:
- Le intestazioni sono impostate su un valore diverso da
"*"
- AllowAnyHeader viene chiamato: includere almeno
Accept
,Content-Type
eOrigin
, più eventuali intestazioni personalizzate che si desidera supportare.
Codice di richiesta preliminare automatico
Quando vengono applicati i criteri CORS:
- A livello globale chiamando
app.UseCors
inProgram.cs
. - Uso dell'attributo
[EnableCors]
.
ASP.NET Core risponde alla richiesta OPTIONS preliminare.
L'abilitazione di CORS in base all'endpoint con RequireCors
attualmente non supporta le richieste preliminari automatiche.
La sezione Test CORS di questo documento illustra questo comportamento.
Attributo [HttpOptions] per le richieste preliminari
Quando CORS è abilitato con i criteri appropriati, ASP.NET Core risponde in genere alle richieste preliminari CORS automaticamente. In alcuni scenari, questo potrebbe non essere il caso. Ad esempio, usando CORS con il routing degli endpoint.
Il codice seguente usa l'attributo [HttpOptions] per creare endpoint per le richieste OPTIONS:
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
Vedere Testare CORS con il routing degli endpoint e [HttpOptions] per istruzioni sul test del codice precedente.
Impostare l'ora di scadenza preliminare
L'intestazione Access-Control-Max-Age
specifica per quanto tempo è possibile memorizzare nella cache la risposta alla richiesta preliminare. Per impostare questa intestazione, chiamare SetPreflightMaxAge:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MySetPreflightExpirationPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
});
});
builder.Services.AddControllers();
var app = builder.Build();
Funzionamento di CORS
Questa sezione descrive cosa accade in una richiesta CORS a livello di messaggi HTTP.
- CORS non è una funzionalità di sicurezza. CORS è uno standard W3C che consente a un server di ridurre i criteri di stessa origine.
- Ad esempio, un attore malintenzionato potrebbe usare xss (Cross-Site Scripting) sul sito ed eseguire una richiesta tra siti al sito abilitato per CORS per rubare informazioni.
- Un'API non è più sicura consentendo CORS.
- Spetta al client (browser) applicare CORS. Il server esegue la richiesta e restituisce la risposta, è il client che restituisce un errore e blocca la risposta. Ad esempio, uno degli strumenti seguenti visualizzerà la risposta del server:
- Fiddler
- .NET HttpClient
- Un Web browser immettendo l'URL nella barra degli indirizzi.
- Spetta al client (browser) applicare CORS. Il server esegue la richiesta e restituisce la risposta, è il client che restituisce un errore e blocca la risposta. Ad esempio, uno degli strumenti seguenti visualizzerà la risposta del server:
- È un modo per consentire ai browser di eseguire una richiesta XHR o Fetch API di origine incrociata che altrimenti sarebbe vietata.
- I browser senza CORS non possono eseguire richieste tra le origini. Prima di CORS, JSONP veniva usato per aggirare questa restrizione. JSONP non usa XHR, ma usa il
<script>
tag per ricevere la risposta. Gli script possono essere caricati tra le origini.
- 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
La specifica CORS ha introdotto diverse nuove intestazioni HTTP che abilitano le richieste tra le origini. Se un browser supporta CORS, imposta automaticamente queste intestazioni per le richieste tra le origini. Il codice JavaScript personalizzato non è necessario per abilitare CORS.
Di seguito è riportato un esempio di richiesta tra le origini dal pulsante di test Values a https://cors1.azurewebsites.net/api/values
. Intestazione Origin
:
- Fornisce il dominio del sito che effettua la richiesta.
- È obbligatorio e deve essere diverso dall'host.
Intestazioni generali
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
Intestazioni di risposta
Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET
Intestazioni delle richieste
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...
Nelle OPTIONS
richieste, il server imposta l'intestazione Intestazioni Access-Control-Allow-Origin: {allowed origin}
risposta nella risposta. Ad esempio, l'esempio distribuito, la richiesta del pulsante OPTIONS
Delete [EnableCors] contiene le intestazioni seguenti:
Intestazioni generali
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
Intestazioni di risposta
Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET
Intestazioni delle richieste
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
Nelle intestazioni response precedenti il server imposta l'intestazione Access-Control-Allow-Origin nella risposta. Il https://cors1.azurewebsites.net
valore di questa intestazione corrisponde all'intestazione Origin
della richiesta.
Se AllowAnyOrigin viene chiamato , Access-Control-Allow-Origin: *
viene restituito il valore con caratteri jolly . AllowAnyOrigin
consente qualsiasi origine.
Se la risposta non include l'intestazione Access-Control-Allow-Origin
, la richiesta tra le origini ha esito negativo. In particolare, il browser non consente la richiesta. Anche se il server restituisce una risposta corretta, il browser non rende disponibile la risposta all'app client.
Il reindirizzamento HTTP a HTTPS causa ERR_INVALID_REDIRECT nella richiesta preliminare CORS
Le richieste a un endpoint che usano HTTP reindirizzate a HTTPS UseHttpsRedirection hanno esito negativo con ERR_INVALID_REDIRECT on the CORS preflight request
.
I progetti API possono rifiutare le richieste HTTP anziché usarle UseHttpsRedirection
per reindirizzare le richieste a HTTPS.
Visualizzare le richieste OPTIONS
Per impostazione predefinita, i browser Chrome e Edge non visualizzano le richieste OPTIONS nella scheda di rete degli strumenti F12. Per visualizzare le richieste OPTIONS nei browser seguenti:
chrome://flags/#out-of-blink-cors
oppureedge://flags/#out-of-blink-cors
- disabilitare il flag.
- riavviare.
Firefox mostra le richieste OPTIONS per impostazione predefinita.
CORS in IIS
Quando si esegue la distribuzione in IIS, CORS deve essere eseguito prima dell'autenticazione di Windows se il server non è configurato per consentire l'accesso anonimo. Per supportare questo scenario, è necessario installare e configurare il modulo CORS IIS per l'app.
Testare CORS
Il download di esempio include codice per testare CORS. Vedere come scaricare un esempio. L'esempio è un progetto API con Razor Pages aggiunto:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.MapRazorPages();
app.Run();
Avviso
WithOrigins("https://localhost:<port>");
deve essere usato solo per testare un'app di esempio simile al codice di esempio di download.
Di seguito ValuesController
vengono forniti gli endpoint per il test:
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
MyDisplayRouteInfo viene fornito dal pacchetto NuGet Rick.Docs.Samples.RouteInfo e visualizza le informazioni sulla route.
Testare il codice di esempio precedente usando uno degli approcci seguenti:
- Eseguire l'esempio con
dotnet run
usando l'URL predefinito dihttps://localhost:5001
. - Eseguire l'esempio da Visual Studio con la porta impostata su 44398 per un URL di
https://localhost:44398
.
Uso di un browser con gli strumenti F12:
Selezionare il pulsante Valori ed esaminare le intestazioni nella scheda Rete .
Selezionare il pulsante PUT test.Select the PUT test button. Per istruzioni sulla visualizzazione della richiesta OPTIONS, vedere Visualizzare le richieste OPTIONS. Il test PUT crea due richieste, una richiesta preliminare OPTIONS e la richiesta PUT.
Selezionare il
GetValues2 [DisableCors]
pulsante per attivare una richiesta CORS non riuscita. Come accennato nel documento, la risposta restituisce 200 risultati positivi, ma la richiesta CORS non viene effettuata. Selezionare la scheda Console per visualizzare l'errore CORS. A seconda del browser, viene visualizzato un errore simile al seguente:L'accesso al recupero dall'origine
'https://cors1.azurewebsites.net/api/values/GetValues2'
'https://cors3.azurewebsites.net'
è stato bloccato dai criteri CORS: nessuna intestazione "Access-Control-Allow-Origin" è presente nella risorsa richiesta. Se si ritiene soddisfacente una risposta opaca, impostare la modalità della richiesta 'no-cors' per recuperare la risorsa con CORS disabilitato.
Gli endpoint abilitati per CORS possono essere testati con uno strumento, ad esempio curl o Fiddler. Quando si usa uno strumento, l'origine della richiesta specificata dall'intestazione Origin
deve essere diversa dall'host che riceve la richiesta. Se la richiesta non è tra le origini in base al valore dell'intestazione Origin
:
- Non è necessario che il middleware CORS elabori la richiesta.
- Le intestazioni CORS non vengono restituite nella risposta.
Il comando seguente usa curl
per emettere una richiesta OPTIONS con informazioni:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
Testare CORS con il routing degli endpoint e [HttpOptions]
L'abilitazione di CORS in base all'endpoint con RequireCors
attualmente non supporta le richieste preliminari automatiche. Considerare il codice seguente che usa il routing degli endpoint per abilitare CORS:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.MapRazorPages();
app.Run();
Di seguito TodoItems1Controller
sono riportati gli endpoint per il test:
[Route("api/[controller]")]
[ApiController]
public class TodoItems1Controller : ControllerBase
{
// PUT: api/TodoItems1/5
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return Content($"ID = {id}");
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// Delete: api/TodoItems1/5
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/TodoItems1
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors]
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
// Delete: api/TodoItems1/MyDelete2/5
[EnableCors]
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
Testare il codice precedente dalla pagina di test (https://cors1.azurewebsites.net/test?number=1
) dell'esempio distribuito.
I pulsanti Delete [EnableCors] e GET [EnableCors] hanno esito positivo, perché gli endpoint hanno [EnableCors]
e rispondono alle richieste preliminari. Gli altri endpoint hanno esito negativo. Il pulsante GET ha esito negativo perché JavaScript invia:
headers: {
"Content-Type": "x-custom-header"
},
Di seguito TodoItems2Controller
sono disponibili endpoint simili, ma include codice esplicito per rispondere alle richieste OPTIONS:
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// [EnableCors] // Not needed as OPTIONS path provided
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
[EnableCors] // Rquired for this path
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors] // Rquired for this path
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
Il codice precedente può essere testato distribuendo l'esempio in Azure.In'elenco a discesa Controller, selezionare Preflight (Verifica preliminare) e quindi Set Controller (Imposta controller). Tutte le chiamate CORS agli TodoItems2Controller
endpoint hanno esito positivo.
Risorse aggiuntive
Di Rick Anderson e Kirk Larkin
Questo articolo illustra come abilitare CORS in un'app core ASP.NET.
La sicurezza del browser impedisce a una pagina Web di effettuare richieste a un dominio diverso da quello che ha distribuito la pagina Web. Questa restrizione è nota come criteri di corrispondenza dell'origine. I criteri di corrispondenza dell'origine impediscono a un sito dannoso di accedere a dati sensibili in un altro sito. In alcuni casi, potrebbe essere necessario consentire ad altri siti di effettuare richieste tra origini all'app. Per altre informazioni, vedere l'articolo Mozilla CORS.
Condivisione di risorse tra le origini (CORS):
- È uno standard W3C che consente a un server di rilassare i criteri di stessa origine.
- Non è una funzionalità di sicurezza, CORS riduce la sicurezza. Un'API non è più sicura consentendo CORS. Per altre informazioni, vedere Funzionamento di CORS.
- Consente a un server di consentire in modo esplicito alcune richieste tra le origini rifiutando altre.
- È più sicuro e più flessibile rispetto alle tecniche precedenti, ad esempio JSONP.
Visualizzare o scaricare il codice di esempio (procedura per il download)
Stessa origine
Due URL hanno la stessa origine se hanno schemi, host e porte identici (RFC 6454).
Questi due URL hanno la stessa origine:
https://example.com/foo.html
https://example.com/bar.html
Questi URL hanno origini diverse rispetto ai due URL precedenti:
https://example.net
: dominio diversohttps://www.example.com/foo.html
: sottodominio diversohttp://example.com/foo.html
: schema diversohttps://example.com:9000/foo.html
: porta diversa
Abilitare CORS
Esistono tre modi per abilitare CORS:
- Nel middleware usando un criterio denominato o un criterio predefinito.
- Uso del routing degli endpoint.
- Con l'attributo [EnableCors].
L'uso dell'attributo [EnableCors] con un criterio denominato consente di controllare meglio gli endpoint che supportano CORS.
Avviso
UseCors deve essere chiamato nell'ordine corretto. Per altre informazioni, vedere Ordine del middleware. Ad esempio, UseCors
deve essere chiamato prima UseResponseCaching di quando si usa UseResponseCaching
.
Ogni approccio è dettagliato nelle sezioni seguenti.
CORS con criteri e middleware denominati
Il middleware CORS gestisce le richieste tra le origini. Il codice seguente applica un criterio CORS a tutti gli endpoint dell'app con le origini specificate:
public class Startup
{
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
// app.UseResponseCaching();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Il codice precedente:
- Imposta il nome del criterio su
_myAllowSpecificOrigins
. Il nome del criterio è arbitrario. - Chiama il UseCors metodo di estensione e specifica i
_myAllowSpecificOrigins
criteri CORS.UseCors
aggiunge il middleware CORS. La chiamata aUseCors
deve essere inserita dopoUseRouting
, ma primaUseAuthorization
di . Per altre informazioni, vedere Ordine del middleware. - Chiamate AddCors con un'espressione lambda. L'espressione lambda accetta un CorsPolicyBuilder oggetto . Le opzioni di configurazione, ad esempio
WithOrigins
, sono descritte più avanti in questo articolo. - Abilita i
_myAllowSpecificOrigins
criteri CORS per tutti gli endpoint controller. Vedere Routing degli endpoint per applicare un criterio CORS a endpoint specifici. - Quando si usa il middleware di memorizzazione nella cache delle risposte, chiamare UseCors prima UseResponseCachingdi .
Con il routing degli endpoint, il middleware CORS deve essere configurato per l'esecuzione tra le chiamate a UseRouting
e UseEndpoints
.
Per istruzioni sul test del codice simile al codice precedente, vedere Testare CORS .
La AddCors chiamata al metodo aggiunge servizi CORS al contenitore del servizio dell'app:
public class Startup
{
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
services.AddControllers();
}
Per altre informazioni, vedere Opzioni dei criteri CORS in questo documento.
I CorsPolicyBuilder metodi possono essere concatenati, come illustrato nel codice seguente:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddControllers();
}
Nota: l'URL specificato non deve contenere una barra finale (/
). Se l'URL termina con /
, il confronto restituisce false
e non viene restituita alcuna intestazione.
CORS con criteri e middleware predefiniti
Il codice evidenziato seguente abilita i criteri CORS predefiniti:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddDefaultPolicy(
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Il codice precedente applica i criteri CORS predefiniti a tutti gli endpoint controller.
Abilitare Cors con il routing degli endpoint
L'abilitazione di CORS per endpoint tramite RequireCors
non supporta le richieste preliminari automatiche. Per altre informazioni, vedere questo problema di GitHub e Testare CORS con il routing degli endpoint e [HttpOptions].
Con il routing degli endpoint, CORS può essere abilitato per ogni endpoint usando il RequireCors set di metodi di estensione:
public class Startup
{
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
services.AddControllers();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/echo",
context => context.Response.WriteAsync("echo"))
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapControllers()
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapGet("/echo2",
context => context.Response.WriteAsync("echo2"));
endpoints.MapRazorPages();
});
}
}
Nel codice precedente:
app.UseCors
abilita il middleware CORS. Poiché non è stato configurato un criterio predefinito,app.UseCors()
da solo non abilita CORS.- Gli
/echo
endpoint del controller e consentono le richieste tra le origini usando i criteri specificati. - Gli
/echo2
endpoint e Razor Pages non consentono le richieste tra le origini perché non è stato specificato alcun criterio predefinito.
L'attributo [DisableCors] non disabilita CORS abilitato dal routing degli endpoint con RequireCors
.
Vedere Testare CORS con il routing degli endpoint e [HttpOptions] per istruzioni sul test del codice simile al precedente.
Abilitare CORS con attributi
L'abilitazione di CORS con l'attributo [EnableCors] e l'applicazione di un criterio denominato solo agli endpoint che richiedono CORS fornisce il controllo migliore.
L'attributo [EnableCors] offre un'alternativa all'applicazione globale di CORS. L'attributo [EnableCors]
abilita CORS per gli endpoint selezionati, anziché per tutti gli endpoint:
[EnableCors]
specifica il criterio predefinito.[EnableCors("{Policy String}")]
specifica un criterio denominato.
L'attributo [EnableCors]
può essere applicato a:
- Razor Pagina
PageModel
- Controller
- Metodo di azione del controller
È possibile applicare criteri diversi ai controller, ai modelli di pagina o ai metodi di azione con l'attributo [EnableCors]
. Quando l'attributo [EnableCors]
viene applicato a un controller, a un modello di pagina o a un metodo di azione e CORS è abilitato nel middleware, vengono applicati entrambi i criteri. È consigliabile combinare i criteri. Usare[EnableCors]
attributo o middleware, non entrambi nella stessa app.
Il codice seguente applica criteri diversi a ogni metodo:
[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
// GET api/values
[EnableCors("AnotherPolicy")]
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "green widget", "red widget" };
}
// GET api/values/5
[EnableCors("Policy1")]
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return id switch
{
1 => "green widget",
2 => "red widget",
_ => NotFound(),
};
}
}
Il codice seguente crea due criteri CORS:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("Policy1",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
options.AddPolicy("AnotherPolicy",
policy =>
{
policy.WithOrigins("http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Per il controllo più dettagliato della limitazione delle richieste CORS:
- Usare
[EnableCors("MyPolicy")]
con un criterio denominato. - Non definire un criterio predefinito.
- Non usare il routing degli endpoint.
Il codice nella sezione successiva soddisfa l'elenco precedente.
Per istruzioni sul test del codice simile al codice precedente, vedere Testare CORS .
Disabilitare CORS
L'attributo [DisableCors] non disabilita CORS abilitato dal routing degli endpoint.
Il codice seguente definisce i criteri CORS "MyPolicy"
:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.WithMethods("PUT", "DELETE", "GET");
});
});
services.AddControllers();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapRazorPages();
});
}
}
Il codice seguente disabilita CORS per l'azione GetValues2
:
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
Il codice precedente:
- Non abilita CORS con il routing degli endpoint.
- Non definisce un criterio CORS predefinito.
- Usa [EnableCors("MyPolicy")] per abilitare i
"MyPolicy"
criteri CORS per il controller. - Disabilita CORS per il
GetValues2
metodo .
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:
- Impostare le origini consentite
- Impostare i metodi HTTP consentiti
- Impostare le intestazioni di richiesta consentite
- Impostare le intestazioni di risposta esposte
- Credenziali nelle richieste tra le origini
- Impostare l'ora di scadenza preliminare
AddPolicy viene chiamato in Startup.ConfigureServices
. Per alcune opzioni, può essere utile leggere prima la sezione Funzionamento di CORS .
Impostare le origini consentite
AllowAnyOrigin: consente le richieste CORS da tutte le origini con qualsiasi schema (http
o https
). AllowAnyOrigin
non è sicuro perché qualsiasi sito Web può effettuare richieste tra le origini all'app.
Nota
Specificare AllowAnyOrigin
e AllowCredentials
è una configurazione non sicura e può comportare la falsificazione della richiesta intersito. Il servizio CORS restituisce una risposta CORS non valida quando un'app è configurata con entrambi i metodi.
AllowAnyOrigin
influisce sulle richieste preliminari e sull'intestazione Access-Control-Allow-Origin
. Per altre informazioni, vedere la sezione Richieste preliminari .
SetIsOriginAllowedToAllowWildcardSubdomains: imposta la IsOriginAllowed proprietà del criterio come funzione che consente alle origini di trovare una corrispondenza con un dominio con caratteri jolly configurato durante la valutazione se l'origine è consentita.
options.AddPolicy("MyAllowSubdomainPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
Impostare i metodi HTTP consentiti
- Consente qualsiasi metodo HTTP:
- Influisce sulle richieste preliminari e sull'intestazione
Access-Control-Allow-Methods
. Per altre informazioni, vedere la sezione Richieste preliminari .
Impostare le intestazioni di richiesta consentite
Per consentire l'invio di intestazioni specifiche in una richiesta CORS, denominata intestazioni di richiesta autore, chiamare WithHeaders e specificare le intestazioni consentite:
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
// requires using Microsoft.Net.Http.Headers;
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
Per consentire tutte le intestazioni di richiesta dell'autore, chiamare AllowAnyHeader:
options.AddPolicy("MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
AllowAnyHeader
influisce sulle richieste preliminari e sull'intestazione Access-Control-Request-Headers . Per altre informazioni, vedere la sezione Richieste preliminari .
Un criterio middleware CORS corrisponde a intestazioni specifiche specificate da WithHeaders
è possibile solo quando le intestazioni inviate in Access-Control-Request-Headers
corrispondono esattamente alle intestazioni indicate in WithHeaders
.
Si consideri, ad esempio, un'app configurata come segue:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
Il middleware CORS rifiuta una richiesta preliminare con l'intestazione di richiesta seguente perché Content-Language
(HeaderNames.ContentLanguage) non è elencato in WithHeaders
:
Access-Control-Request-Headers: Cache-Control, Content-Language
L'app restituisce una risposta 200 OK , ma non invia di nuovo le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra le origini.
Impostare le intestazioni di risposta esposte
Per impostazione predefinita, il browser non espone tutte le intestazioni di risposta all'app. Per altre informazioni, vedere W3C Cross-Origin Resource Sharing (Terminologia): Intestazione di risposta semplice.
Le intestazioni di risposta disponibili per impostazione predefinita sono:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
La specifica CORS chiama queste intestazioni semplici intestazioni di risposta. Per rendere disponibili altre intestazioni all'app, chiamare WithExposedHeaders:
options.AddPolicy("MyExposeResponseHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.WithExposedHeaders("x-custom-header");
});
Credenziali nelle richieste tra le origini
Le credenziali richiedono una gestione speciale in una richiesta CORS. Per impostazione predefinita, il browser non invia le credenziali con una richiesta tra le origini. Le credenziali includono cookie e schemi di autenticazione HTTP. Per inviare le credenziali con una richiesta tra le origini, il client deve impostare su XMLHttpRequest.withCredentials
true
.
Uso XMLHttpRequest
diretto:
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
Uso di jQuery:
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
Il server deve consentire le credenziali. Per consentire le credenziali tra le origini, chiamare AllowCredentials:
options.AddPolicy("MyMyAllowCredentialsPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.AllowCredentials();
});
La risposta HTTP include un'intestazione Access-Control-Allow-Credentials
che indica al browser che il server consente le credenziali per una richiesta tra le origini.
Se il browser invia credenziali ma la risposta non include un'intestazione valida Access-Control-Allow-Credentials
, il browser non espone la risposta all'app e la richiesta tra le origini ha esito negativo.
Consentire le credenziali tra le origini è un rischio per la sicurezza. Un sito Web in un altro dominio può inviare le credenziali di un utente connesso all'app per conto dell'utente senza conoscere l'utente.
La specifica CORS indica inoltre che l'impostazione delle origini su "*"
(tutte le origini) non è valida se l'intestazione Access-Control-Allow-Credentials
è presente.
Richieste preliminari
Per alcune richieste CORS, il browser invia una richiesta OPTIONS aggiuntiva prima di effettuare la richiesta effettiva. Questa richiesta viene chiamata richiesta preliminare. Il browser può ignorare la richiesta preliminare se sono soddisfatte tutte le condizioni seguenti:
- Il metodo di richiesta è GET, HEAD o POST.
- L'app non imposta intestazioni di richiesta diverse da
Accept
,Accept-Language
,Content-Language
Content-Type
, oLast-Event-ID
. - L'intestazione
Content-Type
, se impostata, ha uno dei valori seguenti:application/x-www-form-urlencoded
multipart/form-data
text/plain
La regola sulle intestazioni di richiesta impostata per la richiesta client si applica alle intestazioni impostate dall'app chiamando setRequestHeader
sull'oggetto XMLHttpRequest
. La specifica CORS chiama queste intestazioni le intestazioni di richiesta dell'autore. La regola non si applica alle intestazioni che il browser può impostare, ad esempio User-Agent
, Host
o 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:
- Access-Control-Request-Method: metodo HTTP che verrà usato per la richiesta effettiva.
- Access-Control-Request-Headers: elenco di intestazioni di richiesta impostate dall'app nella richiesta effettiva. Come indicato in precedenza, non include intestazioni impostate dal browser, ad esempio
User-Agent
. - Access-Control-Allow-Methods
Se la richiesta preliminare viene negata, l'app restituisce una 200 OK
risposta ma non imposta le intestazioni CORS. Pertanto, il browser non tenta la richiesta tra le origini. Per un esempio di richiesta preliminare negata, vedere la sezione Test CORS di questo documento.
Usando gli strumenti F12, l'app console mostra un errore simile a uno dei seguenti, a seconda del browser:
- Firefox: Richiesta tra le origini bloccate: lo stesso criterio di origine non consente la lettura della risorsa remota in
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Motivo: richiesta CORS non riuscita). Altre informazioni - Basato su Chromium: l'accesso al recupero all'origine 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' dall'origine 'https://cors3.azurewebsites.net' è stato bloccato dai criteri CORS: la risposta alla richiesta preliminare non supera il controllo di accesso: nessuna intestazione 'Access-Control-Allow-Origin' è presente nella risorsa richiesta. Se si ritiene soddisfacente una risposta opaca, impostare la modalità della richiesta 'no-cors' per recuperare la risorsa con CORS disabilitato.
Per consentire intestazioni specifiche, chiamare WithHeaders:
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
// requires using Microsoft.Net.Http.Headers;
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
Per consentire tutte le intestazioni di richiesta dell'autore, chiamare AllowAnyHeader:
options.AddPolicy("MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
I browser non sono coerenti nel modo in cui impostano Access-Control-Request-Headers
. In caso affermativo:
- Le intestazioni sono impostate su un valore diverso da
"*"
- AllowAnyHeader viene chiamato: includere almeno
Accept
,Content-Type
eOrigin
, più eventuali intestazioni personalizzate che si desidera supportare.
Codice di richiesta preliminare automatico
Quando vengono applicati i criteri CORS:
- A livello globale chiamando
app.UseCors
inStartup.Configure
. - Uso dell'attributo
[EnableCors]
.
ASP.NET Core risponde alla richiesta OPTIONS preliminare.
L'abilitazione di CORS in base all'endpoint con RequireCors
attualmente non supporta le richieste preliminari automatiche.
La sezione Test CORS di questo documento illustra questo comportamento.
Attributo [HttpOptions] per le richieste preliminari
Quando CORS è abilitato con i criteri appropriati, ASP.NET Core risponde in genere alle richieste preliminari CORS automaticamente. In alcuni scenari, questo potrebbe non essere il caso. Ad esempio, usando CORS con il routing degli endpoint.
Il codice seguente usa l'attributo [HttpOptions] per creare endpoint per le richieste OPTIONS:
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
Vedere Testare CORS con il routing degli endpoint e [HttpOptions] per istruzioni sul test del codice precedente.
Impostare l'ora di scadenza preliminare
L'intestazione Access-Control-Max-Age
specifica per quanto tempo è possibile memorizzare nella cache la risposta alla richiesta preliminare. Per impostare questa intestazione, chiamare SetPreflightMaxAge:
options.AddPolicy("MySetPreflightExpirationPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
});
Funzionamento di CORS
Questa sezione descrive cosa accade in una richiesta CORS a livello di messaggi HTTP.
- CORS non è una funzionalità di sicurezza. CORS è uno standard W3C che consente a un server di ridurre i criteri di stessa origine.
- Ad esempio, un attore malintenzionato potrebbe usare xss (Cross-Site Scripting) sul sito ed eseguire una richiesta tra siti al sito abilitato per CORS per rubare informazioni.
- Un'API non è più sicura consentendo CORS.
- Spetta al client (browser) applicare CORS. Il server esegue la richiesta e restituisce la risposta, è il client che restituisce un errore e blocca la risposta. Ad esempio, uno degli strumenti seguenti visualizzerà la risposta del server:
- Fiddler
- .NET HttpClient
- Un Web browser immettendo l'URL nella barra degli indirizzi.
- Spetta al client (browser) applicare CORS. Il server esegue la richiesta e restituisce la risposta, è il client che restituisce un errore e blocca la risposta. Ad esempio, uno degli strumenti seguenti visualizzerà la risposta del server:
- È un modo per consentire ai browser di eseguire una richiesta XHR o Fetch API di origine incrociata che altrimenti sarebbe vietata.
- I browser senza CORS non possono eseguire richieste tra le origini. Prima di CORS, JSONP veniva usato per aggirare questa restrizione. JSONP non usa XHR, ma usa il
<script>
tag per ricevere la risposta. Gli script possono essere caricati tra le origini.
- 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
La specifica CORS ha introdotto diverse nuove intestazioni HTTP che abilitano le richieste tra le origini. Se un browser supporta CORS, imposta automaticamente queste intestazioni per le richieste tra le origini. Il codice JavaScript personalizzato non è necessario per abilitare CORS.
Di seguito è riportato un esempio di richiesta tra le origini dal pulsante di test Values a https://cors1.azurewebsites.net/api/values
. Intestazione Origin
:
- Fornisce il dominio del sito che effettua la richiesta.
- È obbligatorio e deve essere diverso dall'host.
Intestazioni generali
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
Intestazioni di risposta
Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET
Intestazioni delle richieste
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...
Nelle OPTIONS
richieste, il server imposta l'intestazione Intestazioni Access-Control-Allow-Origin: {allowed origin}
risposta nella risposta. Ad esempio, l'esempio distribuito, la richiesta del pulsante OPTIONS
Delete [EnableCors] contiene le intestazioni seguenti:
Intestazioni generali
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
Intestazioni di risposta
Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET
Intestazioni delle richieste
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
Nelle intestazioni response precedenti il server imposta l'intestazione Access-Control-Allow-Origin nella risposta. Il https://cors1.azurewebsites.net
valore di questa intestazione corrisponde all'intestazione Origin
della richiesta.
Se AllowAnyOrigin viene chiamato , Access-Control-Allow-Origin: *
viene restituito il valore con caratteri jolly . AllowAnyOrigin
consente qualsiasi origine.
Se la risposta non include l'intestazione Access-Control-Allow-Origin
, la richiesta tra le origini ha esito negativo. In particolare, il browser non consente la richiesta. Anche se il server restituisce una risposta corretta, il browser non rende disponibile la risposta all'app client.
Visualizzare le richieste OPTIONS
Per impostazione predefinita, i browser Chrome e Edge non visualizzano le richieste OPTIONS nella scheda di rete degli strumenti F12. Per visualizzare le richieste OPTIONS nei browser seguenti:
chrome://flags/#out-of-blink-cors
oppureedge://flags/#out-of-blink-cors
- disabilitare il flag.
- riavviare.
Firefox mostra le richieste OPTIONS per impostazione predefinita.
CORS in IIS
Quando si esegue la distribuzione in IIS, CORS deve essere eseguito prima dell'autenticazione di Windows se il server non è configurato per consentire l'accesso anonimo. Per supportare questo scenario, è necessario installare e configurare il modulo CORS IIS per l'app.
Testare CORS
Il download di esempio include codice per testare CORS. Vedere come scaricare un esempio. L'esempio è un progetto API con Razor Pages aggiunto:
public class StartupTest2
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithMethods("PUT", "DELETE", "GET");
});
});
services.AddControllers();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapRazorPages();
});
}
}
Avviso
WithOrigins("https://localhost:<port>");
deve essere usato solo per testare un'app di esempio simile al codice di esempio di download.
Di seguito ValuesController
vengono forniti gli endpoint per il test:
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
MyDisplayRouteInfo viene fornito dal pacchetto NuGet Rick.Docs.Samples.RouteInfo e visualizza le informazioni sulla route.
Testare il codice di esempio precedente usando uno degli approcci seguenti:
- Eseguire l'esempio con
dotnet run
usando l'URL predefinito dihttps://localhost:5001
. - Eseguire l'esempio da Visual Studio con la porta impostata su 44398 per un URL di
https://localhost:44398
.
Uso di un browser con gli strumenti F12:
Selezionare il pulsante Valori ed esaminare le intestazioni nella scheda Rete .
Selezionare il pulsante PUT test.Select the PUT test button. Per istruzioni sulla visualizzazione della richiesta OPTIONS, vedere Visualizzare le richieste OPTIONS. Il test PUT crea due richieste, una richiesta preliminare OPTIONS e la richiesta PUT.
Selezionare il
GetValues2 [DisableCors]
pulsante per attivare una richiesta CORS non riuscita. Come accennato nel documento, la risposta restituisce 200 risultati positivi, ma la richiesta CORS non viene effettuata. Selezionare la scheda Console per visualizzare l'errore CORS. A seconda del browser, viene visualizzato un errore simile al seguente:L'accesso al recupero dall'origine
'https://cors1.azurewebsites.net/api/values/GetValues2'
'https://cors3.azurewebsites.net'
è stato bloccato dai criteri CORS: nessuna intestazione "Access-Control-Allow-Origin" è presente nella risorsa richiesta. Se si ritiene soddisfacente una risposta opaca, impostare la modalità della richiesta 'no-cors' per recuperare la risorsa con CORS disabilitato.
Gli endpoint abilitati per CORS possono essere testati con uno strumento, ad esempio curl o Fiddler. Quando si usa uno strumento, l'origine della richiesta specificata dall'intestazione Origin
deve essere diversa dall'host che riceve la richiesta. Se la richiesta non è tra le origini in base al valore dell'intestazione Origin
:
- Non è necessario che il middleware CORS elabori la richiesta.
- Le intestazioni CORS non vengono restituite nella risposta.
Il comando seguente usa curl
per emettere una richiesta OPTIONS con informazioni:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
Testare CORS con il routing degli endpoint e [HttpOptions]
L'abilitazione di CORS in base all'endpoint con RequireCors
attualmente non supporta le richieste preliminari automatiche. Considerare il codice seguente che usa il routing degli endpoint per abilitare CORS:
public class StartupEndPointBugTest
{
readonly string MyPolicy = "_myPolicy";
// .WithHeaders(HeaderNames.ContentType, "x-custom-header")
// forces browsers to require a preflight request with GET
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: MyPolicy,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithHeaders(HeaderNames.ContentType, "x-custom-header")
.WithMethods("PUT", "DELETE", "GET", "OPTIONS");
});
});
services.AddControllers();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers().RequireCors(MyPolicy);
endpoints.MapRazorPages();
});
}
}
Di seguito TodoItems1Controller
sono riportati gli endpoint per il test:
[Route("api/[controller]")]
[ApiController]
public class TodoItems1Controller : ControllerBase
{
// PUT: api/TodoItems1/5
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return Content($"ID = {id}");
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// Delete: api/TodoItems1/5
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/TodoItems1
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors]
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
// Delete: api/TodoItems1/MyDelete2/5
[EnableCors]
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
Testare il codice precedente dalla pagina di test (https://cors1.azurewebsites.net/test?number=1
) dell'esempio distribuito.
I pulsanti Delete [EnableCors] e GET [EnableCors] hanno esito positivo, perché gli endpoint hanno [EnableCors]
e rispondono alle richieste preliminari. Gli altri endpoint hanno esito negativo. Il pulsante GET ha esito negativo perché JavaScript invia:
headers: {
"Content-Type": "x-custom-header"
},
Di seguito TodoItems2Controller
sono disponibili endpoint simili, ma include codice esplicito per rispondere alle richieste OPTIONS:
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// [EnableCors] // Not needed as OPTIONS path provided
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
[EnableCors] // Rquired for this path
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors] // Rquired for this path
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
Il codice precedente può essere testato distribuendo l'esempio in Azure.In'elenco a discesa Controller, selezionare Preflight (Verifica preliminare) e quindi Set Controller (Imposta controller). Tutte le chiamate CORS agli TodoItems2Controller
endpoint hanno esito positivo.