Включение запросов CORS в ASP.NET Core
Примечание.
Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 8 этой статьи.
Предупреждение
Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в статье о политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 8 этой статьи.
Внимание
Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
В текущем выпуске см . версию .NET 8 этой статьи.
Авторы: Рик Андерсон (Rick Anderson) и Кирк Ларкин (Kirk Larkin)
В этой статье показано, как Cross-O rigin Resource Sharing (CORS) включен в приложении ASP.NET Core.
Система безопасности браузера предотвращает запросы веб-страницы к другому домену, отличному от того, который обслуживает веб-страницу. Это ограничение называется политика одного источника. Эта политика предотвращает чтение вредоносным сайтом конфиденциальных данных с другого сайта. Иногда вы должны разрешить другим сайтам выполнять запросы к приложению независимо от источника. Дополнительные сведения см. в статье Mozilla CORS.
Общий доступ к ресурсам между источниками (CORS):
- Является стандартом W3C, который позволяет серверу расслабиться в политике одного и того же источника.
- Не является функцией безопасности, CORS смягчает безопасность. API не безопаснее, разрешая CORS. Дополнительные сведения см. в статье о работе CORS.
- Позволяет серверу явно разрешать некоторые запросы между источниками при отклонении других.
- Является более безопасным и более гибким, чем более ранние методы, такие как JSONP.
Просмотреть или скачать образец кода (описание загрузки)
Тот же источник
Два URL-адреса имеют одинаковый источник, если они имеют одинаковые схемы, узлы и порты (RFC 6454).
Эти два URL-адреса имеют одинаковый источник:
https://example.com/foo.html
https://example.com/bar.html
Эти URL-адреса имеют разные источники, отличные от предыдущих двух URL-адресов:
https://example.net
: другой доменhttps://contoso.example.com/foo.html
: другой поддоменhttp://example.com/foo.html
: другая схемаhttps://example.com:9000/foo.html
: другой порт
Включение CORS
Существует три способа включения CORS:
- В ПО промежуточного слоя с помощью именованной политики или политики по умолчанию.
- Использование маршрутизации конечных точек.
- С атрибутом [EnableCors] .
Использование атрибута [EnableCors] с именованной политикой обеспечивает лучший контроль в ограничении конечных точек, поддерживающих CORS.
Предупреждение
UseCors должен вызываться в правильном порядке. Дополнительные сведения см. в порядке по промежуточного слоя. Например, UseCors
необходимо вызвать перед UseResponseCaching использованием UseResponseCaching
.
Каждый подход подробно описан в следующих разделах.
CORS с именованной политикой и ПО промежуточного слоя
ПО промежуточного слоя CORS обрабатывает запросы между источниками. Следующий код применяет политику CORS ко всем конечным точкам приложения с указанными источниками:
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();
Предыдущий код:
- Задает для
_myAllowSpecificOrigins
имени политики значение . Имя политики является произвольным. - UseCors Вызывает метод расширения и задает
_myAllowSpecificOrigins
политику CORS.UseCors
добавляет ПО промежуточного слоя CORS.UseCors
Вызов должен быть помещен послеUseRouting
, но доUseAuthorization
. Дополнительные сведения см. в порядке по промежуточного слоя. - Вызовы AddCors с лямбда-выражением. Лямбда принимает CorsPolicyBuilder объект. Параметры конфигурации, например
WithOrigins
, описаны далее в этой статье. _myAllowSpecificOrigins
Включает политику CORS для всех конечных точек контроллера. См. маршрутизацию конечных точек, чтобы применить политику CORS к определенным конечным точкам.- При использовании по промежуточного слоя кэширования ответов вызовите UseCors раньше UseResponseCaching.
При маршрутизации конечных точек ПО промежуточного слоя CORS необходимо настроить для выполнения между вызовами UseRouting
и UseEndpoints
.
Вызов AddCors метода добавляет службы CORS в контейнер службы приложения:
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();
Дополнительные сведения см. в разделе "Параметры политики CORS" в этом документе.
Методы CorsPolicyBuilder можно связать, как показано в следующем коде:
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();
Примечание. Указанный URL-адрес не должен содержать косую черту (/
). Если URL-адрес завершается /
, сравнение возвращается false
и заголовок не возвращается.
Порядок UseCors и UseStaticFiles
Как правило, UseStaticFiles
вызывается раньше UseCors
. Приложения, использующие JavaScript для получения статических файлов, должны вызываться перед UseStaticFiles
вызовомUseCors
.
CORS с политикой по умолчанию и ПО промежуточного слоя
Следующий выделенный код включает политику CORS по умолчанию:
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();
Приведенный выше код применяет политику CORS по умолчанию ко всем конечным точкам контроллера.
Включение CORS с маршрутизацией конечных точек
При маршрутизации конечных точек CORS можно включить на основе каждой конечной точки с помощью RequireCors набора методов расширения:
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();
В предыдущем коде:
app.UseCors
включает ПО промежуточного слоя CORS. Так как политика по умолчанию не настроена,app.UseCors()
только не включает CORS.- Конечные
/echo
точки и конечные точки контроллера разрешают запросы между источниками с помощью указанной политики. - Конечные
/echo2
точки и Razor страницы не разрешают запросы между источниками, так как политика по умолчанию не указана.
Атрибут [DisableCors] не отключает CORS, включенную маршрутизацией конечных точек.RequireCors
Ознакомьтесь с атрибутом Test CORS с атрибутом [EnableCors] и методом RequireCors для инструкций по тестированию кода, аналогичного приведенному выше.
Включение CORS с атрибутами
Включение CORS с атрибутом [EnableCors] и применение именованной политики только к тем конечным точкам, которым требуется CORS, обеспечивает лучший контроль.
Атрибут [EnableCors] предоставляет альтернативу применению CORS глобально. Атрибут [EnableCors]
включает CORS для выбранных конечных точек, а не для всех конечных точек:
[EnableCors]
указывает политику по умолчанию.[EnableCors("{Policy String}")]
указывает именованную политику.
Атрибут [EnableCors]
можно применить к:
- Razor Страница
PageModel
- Контроллер
- Метод действия контроллера
Различные политики можно применять к контроллерам, моделям страниц или методам действий с атрибутом [EnableCors]
. [EnableCors]
Если атрибут применяется к контроллеру, модели страницы или методу действия, а CORS включен в ПО промежуточного слоя, применяются обе политики. Рекомендуется объединять политики. Используйте[EnableCors]
атрибут или ПО промежуточного слоя, а не в одном приложении.
Следующий код применяет другую политику к каждому методу:
[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(),
};
}
}
Следующий код создает две политики 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();
Для лучшего контроля ограничения запросов CORS:
- Используется
[EnableCors("MyPolicy")]
с именованной политикой. - Не определяйте политику по умолчанию.
- Не используйте маршрутизацию конечных точек.
Код в следующем разделе соответствует предыдущему списку.
Отключение CORS
Атрибут [DisableCors] не отключает CORS, включенную маршрутизацией конечных точек.
Следующий код определяет политику "MyPolicy"
CORS:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapControllers();
endpoints.MapRazorPages();
});
app.Run();
Следующий код отключает CORS для 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();
}
Предыдущий код:
- Не включает CORS с маршрутизацией конечных точек.
- Не определяет политику CORS по умолчанию.
- Использует [EnableCors("MyPolicy")] для включения
"MyPolicy"
политики CORS для контроллера. - Отключает CORS для
GetValues2
метода.
Сведения о тестировании предыдущего кода см. в разделе Test CORS .
Параметры политики CORS
В этом разделе описаны различные параметры, которые можно задать в политике CORS:
- Настройка разрешенных источников
- Установка разрешенных методов HTTP
- Настройка заголовков разрешенных запросов
- Настройка заголовков ответов, предоставляемых
- Учетные данные в запросах между источниками
- Установка времени окончания срока действия предварительного срока действия
AddPolicy вызывается в Program.cs
. Для некоторых вариантов может быть полезно сначала ознакомиться с разделом о работе CORS.
Настройка разрешенных источников
AllowAnyOrigin: разрешает запросы CORS из всех источников с любой схемой (http
или https
). AllowAnyOrigin
небезопасно, так как любой веб-сайт может выполнять запросы между источниками приложения.
Примечание.
Указание AllowAnyOrigin
и AllowCredentials
является небезопасной конфигурацией и может привести к подделке межсайтовых запросов. Служба CORS возвращает недопустимый ответ CORS, если приложение настроено с использованием обоих методов.
AllowAnyOrigin
влияет на предварительные Access-Control-Allow-Origin
запросы и заголовок. Дополнительные сведения см. в разделе "Предварительные запросы ".
SetIsOriginAllowedToAllowWildcardSubdomains: задает IsOriginAllowed свойство политики, которая позволяет источникам сопоставлять настроенный домен подстановочных знаков при оценке допустимости источника.
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();
Установка разрешенных методов HTTP
- Разрешает любой метод HTTP:
- Влияет на предварительные
Access-Control-Allow-Methods
запросы и заголовок. Дополнительные сведения см. в разделе "Предварительные запросы ".
Настройка заголовков разрешенных запросов
Чтобы разрешить отправку определенных заголовков в запросе CORS, называется заголовками запросов автора, вызовом WithHeaders и указанием разрешенных заголовков:
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();
Чтобы разрешить все заголовки запросов автора, вызовите 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
влияет на предварительные запросы и заголовок Access-Control-Request-Headers . Дополнительные сведения см. в разделе "Предварительные запросы ".
Политика ПО промежуточного слоя CORS соответствует определенным заголовкам, указанным WithHeaders
только в том случае, если заголовки, отправленные точно Access-Control-Request-Headers
соответствуют заголовкам, указанным в .WithHeaders
Например, рассмотрим приложение, настроенное следующим образом:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
ПО промежуточного слоя CORS отклоняет предварительный запрос со следующим заголовком запроса, так как Content-Language
(HeaderNames.ContentLanguage) не указан вWithHeaders
:
Access-Control-Request-Headers: Cache-Control, Content-Language
Приложение возвращает ответ 200 OK , но не отправляет заголовки CORS обратно. Поэтому браузер не пытается выполнить запрос между источниками.
Настройка заголовков ответов, предоставляемых
По умолчанию браузер не предоставляет все заголовки ответа приложению. Дополнительные сведения см. в разделе "Общий доступ к ресурсам между источниками W3C" (терминология): простой заголовок ответа.
Заголовки ответов, доступные по умолчанию:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
Спецификация CORS вызывает эти заголовки простых заголовков ответа. Чтобы сделать другие заголовки доступными для приложения, вызовите 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();
Учетные данные в запросах между источниками
Учетные данные требуют специальной обработки в запросе CORS. По умолчанию браузер не отправляет учетные данные с запросом между источниками. Учетные данные включают файлы cookie и схемы проверки подлинности HTTP. Чтобы отправить учетные данные с запросом между источниками, клиент должен задать значение XMLHttpRequest.withCredentials
true
.
Использование XMLHttpRequest
напрямую:
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
Использование jQuery:
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
Использование API получения:
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
Сервер должен разрешить учетные данные. Чтобы разрешить учетные данные между источниками, вызовите 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();
Http-ответ содержит заголовок, который сообщает браузеру Access-Control-Allow-Credentials
, что сервер разрешает учетные данные для запроса между источниками.
Если браузер отправляет учетные данные, но ответ не содержит допустимый Access-Control-Allow-Credentials
заголовок, браузер не предоставляет ответ приложению, а запрос между источниками завершается ошибкой.
Разрешение учетных данных между источниками — это риск безопасности. Веб-сайт в другом домене может отправлять учетные данные пользователя, выполнившего вход, в приложение от имени пользователя без знаний пользователя.
Спецификация CORS также указывает, что установка источников "*"
(всех источников) недопустима, если заголовок Access-Control-Allow-Credentials
присутствует.
Предварительные запросы
Для некоторых запросов CORS браузер отправляет дополнительный запрос OPTIONS перед выполнением фактического запроса. Этот запрос называется предварительным запросом. Браузер может пропустить предварительный запрос, если выполняются все следующие условия:
- Метод запроса — GET, HEAD или POST.
- Приложение не задает заголовки запросов, отличные от
Accept
,Accept-Language
, ,Content-Language
Content-Type
илиLast-Event-ID
. - Заголовок
Content-Type
, если задано, имеет одно из следующих значений:application/x-www-form-urlencoded
multipart/form-data
text/plain
Правило заголовков запросов, заданных для клиентского запроса, применяется к заголовкам, заданным приложением путем вызова setRequestHeader
XMLHttpRequest
объекта. Спецификация CORS вызывает эти заголовки для создания запросов. Правило не применяется к заголовкам, которые браузер может задать, например User-Agent
, Host
или Content-Length
.
Примечание.
В этой статье содержатся URL-адреса, созданные путем развертывания примера кода на двух веб-сайтах https://cors3.azurewebsites.net
Azure и https://cors.azurewebsites.net
.
Ниже приведен пример ответа, аналогичного запросу предварительной проверки, сделанному на кнопке [Put test] в разделе Test CORS этого документа.
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
Предварительный запрос использует метод HTTP OPTIONS . Он может включать следующие заголовки:
- Метод Access-Control-Request-Method: метод HTTP, который будет использоваться для фактического запроса.
- Access-Control-Request-Headers: список заголовков запросов, которые приложение задает в фактическом запросе. Как упоминалось ранее, это не включает заголовки, которые задают браузеры, например
User-Agent
.
Если запрос на предварительную 200 OK
проверку запрещен, приложение возвращает ответ, но не задает заголовки CORS. Поэтому браузер не пытается выполнить запрос между источниками. Пример отклоненного предварительного запроса см. в разделе test CORS этого документа.
С помощью средств F12 консольное приложение отображает ошибку, аналогичную одной из следующих, в зависимости от браузера:
- Firefox: запрос между источниками заблокирован: та же политика источника запрещает чтение удаленного ресурса по адресу
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Причина: запрос CORS не выполнен). Подробнее - На основе Chromium: доступ к получению по адресу "https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5" из источникаhttps://cors3.azurewebsites.net "" был заблокирован политикой CORS: ответ на предварительный запрос не передает проверку контроля доступа: в запрошенном ресурсе отсутствует заголовок "Access-Control-Allow-Origin". Если этот непрозрачный ответ вам подходит, задайте для режима запроса значение "no-cors", чтобы извлечь ресурс с отключенным параметром CORS.
Чтобы разрешить определенные заголовки, вызовите 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();
Чтобы разрешить все заголовки запросов автора, вызовите 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();
Браузеры не согласованы в том, как они заданы Access-Control-Request-Headers
. Если одно из следующих вариантов:
- Заголовки имеют значение, отличное от других
"*"
- AllowAnyHeader вызывается: включите по крайней мере
Accept
,Content-Type
иOrigin
, а также все пользовательские заголовки, которые требуется поддерживать.
Код автоматического предварительного запроса
При применении политики CORS:
- Глобально путем вызова
app.UseCors
Program.cs
. - Использование атрибута
[EnableCors]
.
ASP.NET Core отвечает на запрос параметров предварительной проверки.
В разделе Test CORS этого документа показано это поведение.
Атрибут [HttpOptions] для предварительных запросов
Если CORS включена с соответствующей политикой, ASP.NET Core обычно реагирует на запросы предварительной проверки CORS автоматически.
Следующий код использует атрибут [HttpOptions] для создания конечных точек для запросов 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);
}
Сведения о тестировании предыдущего кода см. в разделе Test CORS с атрибутом [EnableCors] и методОм RequireCors.
Установка времени окончания срока действия предварительного срока действия
Заголовок Access-Control-Max-Age
указывает, сколько времени можно кэшировать ответ на предварительный запрос. Чтобы задать этот заголовок, вызовите 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();
Включение CORS в конечной точке
Как работает CORS
В этом разделе описывается, что происходит в запросе CORS на уровне HTTP-сообщений.
- CORS не является функцией безопасности. CORS — это стандарт W3C, позволяющий серверу расслабиться в политике одного источника.
- Например, злоумышленник может использовать межсайтовые скрипты (XSS) на вашем сайте и выполнить межсайтовый запрос на свой сайт с поддержкой CORS для кражи информации.
- API не безопаснее, разрешая CORS.
- Это до клиента (браузера) для принудительного применения CORS. Сервер выполняет запрос и возвращает ответ, он является клиентом, который возвращает ошибку и блокирует ответ. Например, любой из следующих средств отобразит ответ сервера:
- Fiddler
- HttpClient .NET
- Веб-браузер, введя URL-адрес в адресной строке.
- Это до клиента (браузера) для принудительного применения CORS. Сервер выполняет запрос и возвращает ответ, он является клиентом, который возвращает ошибку и блокирует ответ. Например, любой из следующих средств отобразит ответ сервера:
- Это способ для сервера, чтобы разрешить браузерам выполнять запрос XHR или API получения, который в противном случае будет запрещен.
- Браузеры без CORS не могут выполнять запросы между источниками. Перед CORS JSONP использовался для обхода этого ограничения. JSONP не использует XHR, он использует
<script>
тег для получения ответа. Скрипты могут загружаться между источниками.
- Браузеры без CORS не могут выполнять запросы между источниками. Перед CORS JSONP использовался для обхода этого ограничения. JSONP не использует XHR, он использует
Спецификация CORS представила несколько новых заголовков HTTP, которые позволяют выполнять запросы между источниками. Если браузер поддерживает CORS, он автоматически задает эти заголовки для запросов между источниками. Пользовательский код JavaScript не требуется для включения CORS.
Ниже приведен пример запроса между источниками из кнопки "Тест значений " в https://cors1.azurewebsites.net/api/values
. Заголовок Origin
:
- Предоставляет домен сайта, выполняющего запрос.
- Требуется и должен отличаться от узла.
Общие заголовки
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
Заголовки ответа
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
Заголовки запроса
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 ...
В OPTIONS
запросах сервер задает заголовок заголовков Access-Control-Allow-Origin: {allowed origin}
ответа в ответе. Например, в примере кода Delete [EnableCors]
запрос кнопки OPTIONS
содержит следующие заголовки:
Общие заголовки
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
Заголовки ответа
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
Заголовки запроса
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
В предыдущих заголовках ответа сервер задает заголовок Access-Control-Allow-Origin в ответе. Значение https://cors1.azurewebsites.net
этого заголовка соответствует заголовку Origin
из запроса.
Если AllowAnyOrigin вызывается, Access-Control-Allow-Origin: *
возвращается подстановочное значение. AllowAnyOrigin
разрешает любой источник.
Если ответ не включает Access-Control-Allow-Origin
заголовок, запрос между источниками завершается ошибкой. В частности, браузер запрещает запрос. Даже если сервер возвращает успешный ответ, браузер не делает ответ доступным для клиентского приложения.
Перенаправление HTTP на HTTPS вызывает ERR_INVALID_REDIRECT в предварительном запросе CORS
Запросы к конечной точке с использованием HTTP, перенаправленных на HTTPS, сбоем UseHttpsRedirection ERR_INVALID_REDIRECT on the CORS preflight request
.
Проекты API могут отклонять HTTP-запросы, а не использовать UseHttpsRedirection
для перенаправления запросов на HTTPS.
CORS в IIS
При развертывании в IIS CORS необходимо запустить перед проверкой подлинности Windows, если сервер не настроен для предоставления анонимного доступа. Для поддержки этого сценария необходимо установить и настроить модуль IIS CORS для приложения.
Тестирование CORS
Пример скачивания содержит код для тестирования CORS. См. раздел Практическое руководство. Скачивание файла. Примером является проект API с Razor добавленными страницами:
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();
Предупреждение
WithOrigins("https://localhost:<port>");
следует использовать только для тестирования примера приложения, аналогичного примеру кода скачивания.
ValuesController
Ниже приведены конечные точки для тестирования:
[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 предоставляется пакетом NuGet Rick.Docs.Samples.RouteInfo и отображает информацию о маршруте.
Протестируйте предыдущий пример кода с помощью одного из следующих подходов:
- Запустите пример с
dotnet run
использованием URL-адресаhttps://localhost:5001
по умолчанию. - Запустите пример из Visual Studio с портом 44398 для URL-адреса
https://localhost:44398
.
Использование браузера с инструментами F12:
Нажмите кнопку "Значения" и просмотрите заголовки на вкладке "Сеть".
Нажмите кнопку "Тест PUT ". Сведения о отображении запроса OPTIONS см. в разделе "Запросы ПАРАМЕТРОВ ". Тест PUT создает два запроса, предварительный запрос OPTIONS и запрос PUT.
Нажмите кнопку
GetValues2 [DisableCors]
, чтобы активировать неудачный запрос CORS. Как упоминалось в документе, ответ возвращает 200 успешно, но запрос CORS не выполняется. Перейдите на вкладку "Консоль", чтобы увидеть ошибку CORS. В зависимости от браузера отображается ошибка, аналогичная следующему:Доступ к получению
'https://cors1.azurewebsites.net/api/values/GetValues2'
из источника'https://cors3.azurewebsites.net'
заблокирован политикой CORS: в запрошенном ресурсе отсутствует заголовок Access-Control-Allow-Origin. Если этот непрозрачный ответ вам подходит, задайте для режима запроса значение "no-cors", чтобы извлечь ресурс с отключенным параметром CORS.
Конечные точки с поддержкой CORS можно протестировать с помощью средства, например curl или Fiddler. При использовании средства источник запроса, указанного Origin
заголовком, должен отличаться от узла, получающего запрос. Если запрос не является кросс-источником на основе значения заголовка Origin
:
- Для обработки запроса не требуется ПО промежуточного слоя CORS.
- Заголовки CORS не возвращаются в ответе.
Следующая команда используется curl
для выдачи запроса OPTIONS с информацией:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
Тестирование CORS с помощью атрибута [EnableCors] и метода RequireCors
Рассмотрим следующий код, который использует маршрутизацию конечных точек для включения CORS на основе каждой конечной точки с помощью 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();
Обратите внимание, что только /echo
конечная точка используется RequireCors
для разрешения запросов между источниками с помощью указанной политики. Приведенные ниже контроллеры позволяют CORS использовать атрибут [EnableCors].
TodoItems1Controller
Ниже приведены конечные точки для тестирования:
[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);
}
Кнопки Delete [EnableCors] и GET [EnableCors] успешно выполнены, так как конечные точки имеют [EnableCors]
и отвечают на предварительные запросы. Сбой других конечных точек. Кнопка GET завершается ошибкой, так как JavaScript отправляет:
headers: {
"Content-Type": "x-custom-header"
},
TodoItems2Controller
Ниже приведены аналогичные конечные точки, но содержит явный код для реагирования на запросы 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);
}
Приведенный выше код можно проверить, развернув пример в Azure. В раскрывающемся списке контроллера выберите "Предварительная проверка " и " Задать контроллер". Все вызовы CORS к TodoItems2Controller
конечным точкам успешно выполнены.
Дополнительные ресурсы
Авторы: Рик Андерсон (Rick Anderson) и Кирк Ларкин (Kirk Larkin)
В этой статье показано, как включить CORS в приложении ASP.NET Core.
Система безопасности браузера предотвращает запросы веб-страницы к другому домену, отличному от того, который обслуживает веб-страницу. Это ограничение называется политика одного источника. Эта политика предотвращает чтение вредоносным сайтом конфиденциальных данных с другого сайта. Иногда вы должны разрешить другим сайтам выполнять запросы к приложению независимо от источника. Дополнительные сведения см. в статье Mozilla CORS.
Общий доступ к ресурсам между источниками (CORS):
- Является стандартом W3C, который позволяет серверу расслабиться в политике одного и того же источника.
- Не является функцией безопасности, CORS смягчает безопасность. API не безопаснее, разрешая CORS. Дополнительные сведения см. в статье о работе CORS.
- Позволяет серверу явно разрешать некоторые запросы между источниками при отклонении других.
- Является более безопасным и более гибким, чем более ранние методы, такие как JSONP.
Просмотреть или скачать образец кода (описание загрузки)
Тот же источник
Два URL-адреса имеют одинаковый источник, если они имеют одинаковые схемы, узлы и порты (RFC 6454).
Эти два URL-адреса имеют одинаковый источник:
https://example.com/foo.html
https://example.com/bar.html
Эти URL-адреса имеют разные источники, отличные от предыдущих двух URL-адресов:
https://example.net
: другой доменhttps://www.example.com/foo.html
: другой поддоменhttp://example.com/foo.html
: другая схемаhttps://example.com:9000/foo.html
: другой порт
Включение CORS
Существует три способа включения CORS:
- В ПО промежуточного слоя с помощью именованной политики или политики по умолчанию.
- Использование маршрутизации конечных точек.
- С атрибутом [EnableCors] .
Использование атрибута [EnableCors] с именованной политикой обеспечивает лучший контроль в ограничении конечных точек, поддерживающих CORS.
Предупреждение
UseCors должен вызываться в правильном порядке. Дополнительные сведения см. в порядке по промежуточного слоя. Например, UseCors
необходимо вызвать перед UseResponseCaching использованием UseResponseCaching
.
Каждый подход подробно описан в следующих разделах.
CORS с именованной политикой и ПО промежуточного слоя
ПО промежуточного слоя CORS обрабатывает запросы между источниками. Следующий код применяет политику CORS ко всем конечным точкам приложения с указанными источниками:
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();
Предыдущий код:
- Задает для
_myAllowSpecificOrigins
имени политики значение . Имя политики является произвольным. - UseCors Вызывает метод расширения и задает
_myAllowSpecificOrigins
политику CORS.UseCors
добавляет ПО промежуточного слоя CORS.UseCors
Вызов должен быть помещен послеUseRouting
, но доUseAuthorization
. Дополнительные сведения см. в порядке по промежуточного слоя. - Вызовы AddCors с лямбда-выражением. Лямбда принимает CorsPolicyBuilder объект. Параметры конфигурации, например
WithOrigins
, описаны далее в этой статье. _myAllowSpecificOrigins
Включает политику CORS для всех конечных точек контроллера. См. маршрутизацию конечных точек, чтобы применить политику CORS к определенным конечным точкам.- При использовании по промежуточного слоя кэширования ответов вызовите UseCors раньше UseResponseCaching.
При маршрутизации конечных точек ПО промежуточного слоя CORS необходимо настроить для выполнения между вызовами UseRouting
и UseEndpoints
.
Вызов AddCors метода добавляет службы CORS в контейнер службы приложения:
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();
Дополнительные сведения см. в разделе "Параметры политики CORS" в этом документе.
Методы CorsPolicyBuilder можно связать, как показано в следующем коде:
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();
Примечание. Указанный URL-адрес не должен содержать косую черту (/
). Если URL-адрес завершается /
, сравнение возвращается false
и заголовок не возвращается.
Предупреждение
UseCors
должно быть помещено после UseRouting
и до UseAuthorization
. Это позволяет убедиться, что заголовки CORS включены в ответ как для авторизованных, так и для несанкционированных вызовов.
Порядок UseCors и UseStaticFiles
Как правило, UseStaticFiles
вызывается раньше UseCors
. Приложения, использующие JavaScript для получения статических файлов, должны вызываться перед UseStaticFiles
вызовомUseCors
.
CORS с политикой по умолчанию и ПО промежуточного слоя
Следующий выделенный код включает политику CORS по умолчанию:
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();
Приведенный выше код применяет политику CORS по умолчанию ко всем конечным точкам контроллера.
Включение CORS с маршрутизацией конечных точек
При маршрутизации конечных точек CORS можно включить на основе каждой конечной точки с помощью RequireCors набора методов расширения:
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();
В предыдущем коде:
app.UseCors
включает ПО промежуточного слоя CORS. Так как политика по умолчанию не настроена,app.UseCors()
только не включает CORS.- Конечные
/echo
точки и конечные точки контроллера разрешают запросы между источниками с помощью указанной политики. - Конечные
/echo2
точки и Razor страницы не разрешают запросы между источниками, так как политика по умолчанию не указана.
Атрибут [DisableCors] не отключает CORS, включенную маршрутизацией конечных точек.RequireCors
В ASP.NET Core 7.0 [EnableCors]
атрибут должен передать параметр или предупреждение ASP0023 создается из неоднозначного соответствия маршрута. ASP.NET Core 8.0 и более поздних версий 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);
}
Ознакомьтесь с атрибутом Test CORS с атрибутом [EnableCors] и методом RequireCors для инструкций по тестированию кода, аналогичного приведенному выше.
Включение CORS с атрибутами
Включение CORS с атрибутом [EnableCors] и применение именованной политики только к тем конечным точкам, которым требуется CORS, обеспечивает лучший контроль.
Атрибут [EnableCors] предоставляет альтернативу применению CORS глобально. Атрибут [EnableCors]
включает CORS для выбранных конечных точек, а не для всех конечных точек:
[EnableCors]
указывает политику по умолчанию.[EnableCors("{Policy String}")]
указывает именованную политику.
Атрибут [EnableCors]
можно применить к:
- Razor Страница
PageModel
- Контроллер
- Метод действия контроллера
Различные политики можно применять к контроллерам, моделям страниц или методам действий с атрибутом [EnableCors]
. [EnableCors]
Если атрибут применяется к контроллеру, модели страницы или методу действия, а CORS включен в ПО промежуточного слоя, применяются обе политики. Рекомендуется объединять политики. Используйте[EnableCors]
атрибут или ПО промежуточного слоя, а не в одном приложении.
Следующий код применяет другую политику к каждому методу:
[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(),
};
}
}
Следующий код создает две политики 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();
Для лучшего контроля ограничения запросов CORS:
- Используется
[EnableCors("MyPolicy")]
с именованной политикой. - Не определяйте политику по умолчанию.
- Не используйте маршрутизацию конечных точек.
Код в следующем разделе соответствует предыдущему списку.
Отключение CORS
Атрибут [DisableCors] не отключает CORS, включенную маршрутизацией конечных точек.
Следующий код определяет политику "MyPolicy"
CORS:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapControllers();
endpoints.MapRazorPages();
});
app.Run();
Следующий код отключает CORS для 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();
}
Предыдущий код:
- Не включает CORS с маршрутизацией конечных точек.
- Не определяет политику CORS по умолчанию.
- Использует [EnableCors("MyPolicy")] для включения
"MyPolicy"
политики CORS для контроллера. - Отключает CORS для
GetValues2
метода.
Сведения о тестировании предыдущего кода см. в разделе Test CORS .
Параметры политики CORS
В этом разделе описаны различные параметры, которые можно задать в политике CORS:
- Настройка разрешенных источников
- Установка разрешенных методов HTTP
- Настройка заголовков разрешенных запросов
- Настройка заголовков ответов, предоставляемых
- Учетные данные в запросах между источниками
- Установка времени окончания срока действия предварительного срока действия
AddPolicy вызывается в Program.cs
. Для некоторых вариантов может быть полезно сначала ознакомиться с разделом о работе CORS.
Настройка разрешенных источников
AllowAnyOrigin: разрешает запросы CORS из всех источников с любой схемой (http
или https
). AllowAnyOrigin
небезопасно, так как любой веб-сайт может выполнять запросы между источниками приложения.
Примечание.
Указание AllowAnyOrigin
и AllowCredentials
является небезопасной конфигурацией и может привести к подделке межсайтовых запросов. Служба CORS возвращает недопустимый ответ CORS, если приложение настроено с использованием обоих методов.
AllowAnyOrigin
влияет на предварительные Access-Control-Allow-Origin
запросы и заголовок. Дополнительные сведения см. в разделе "Предварительные запросы ".
SetIsOriginAllowedToAllowWildcardSubdomains: задает IsOriginAllowed свойство политики, которая позволяет источникам сопоставлять настроенный домен подстановочных знаков при оценке допустимости источника.
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();
Установка разрешенных методов HTTP
- Разрешает любой метод HTTP:
- Влияет на предварительные
Access-Control-Allow-Methods
запросы и заголовок. Дополнительные сведения см. в разделе "Предварительные запросы ".
Настройка заголовков разрешенных запросов
Чтобы разрешить отправку определенных заголовков в запросе CORS, называется заголовками запросов автора, вызовом WithHeaders и указанием разрешенных заголовков:
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();
Чтобы разрешить все заголовки запросов автора, вызовите 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
влияет на предварительные запросы и заголовок Access-Control-Request-Headers . Дополнительные сведения см. в разделе "Предварительные запросы ".
Политика ПО промежуточного слоя CORS соответствует определенным заголовкам, указанным WithHeaders
только в том случае, если заголовки, отправленные точно Access-Control-Request-Headers
соответствуют заголовкам, указанным в .WithHeaders
Например, рассмотрим приложение, настроенное следующим образом:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
ПО промежуточного слоя CORS отклоняет предварительный запрос со следующим заголовком запроса, так как Content-Language
(HeaderNames.ContentLanguage) не указан вWithHeaders
:
Access-Control-Request-Headers: Cache-Control, Content-Language
Приложение возвращает ответ 200 OK , но не отправляет заголовки CORS обратно. Поэтому браузер не пытается выполнить запрос между источниками.
Настройка заголовков ответов, предоставляемых
По умолчанию браузер не предоставляет все заголовки ответа приложению. Дополнительные сведения см. в разделе "Общий доступ к ресурсам между источниками W3C" (терминология): простой заголовок ответа.
Заголовки ответов, доступные по умолчанию:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
Спецификация CORS вызывает эти заголовки простых заголовков ответа. Чтобы сделать другие заголовки доступными для приложения, вызовите 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();
Учетные данные в запросах между источниками
Учетные данные требуют специальной обработки в запросе CORS. По умолчанию браузер не отправляет учетные данные с запросом между источниками. Учетные данные включают файлы cookie и схемы проверки подлинности HTTP. Чтобы отправить учетные данные с запросом между источниками, клиент должен задать значение XMLHttpRequest.withCredentials
true
.
Использование XMLHttpRequest
напрямую:
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
Использование jQuery:
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
Использование API получения:
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
Сервер должен разрешить учетные данные. Чтобы разрешить учетные данные между источниками, вызовите 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();
Http-ответ содержит заголовок, который сообщает браузеру Access-Control-Allow-Credentials
, что сервер разрешает учетные данные для запроса между источниками.
Если браузер отправляет учетные данные, но ответ не содержит допустимый Access-Control-Allow-Credentials
заголовок, браузер не предоставляет ответ приложению, а запрос между источниками завершается ошибкой.
Разрешение учетных данных между источниками — это риск безопасности. Веб-сайт в другом домене может отправлять учетные данные пользователя, выполнившего вход, в приложение от имени пользователя без знаний пользователя.
Спецификация CORS также указывает, что установка источников "*"
(всех источников) недопустима, если заголовок Access-Control-Allow-Credentials
присутствует.
Предварительные запросы
Для некоторых запросов CORS браузер отправляет дополнительный запрос OPTIONS перед выполнением фактического запроса. Этот запрос называется предварительным запросом. Браузер может пропустить предварительный запрос, если выполняются все следующие условия:
- Метод запроса — GET, HEAD или POST.
- Приложение не задает заголовки запросов, отличные от
Accept
,Accept-Language
, ,Content-Language
Content-Type
илиLast-Event-ID
. - Заголовок
Content-Type
, если задано, имеет одно из следующих значений:application/x-www-form-urlencoded
multipart/form-data
text/plain
Правило заголовков запросов, заданных для клиентского запроса, применяется к заголовкам, заданным приложением путем вызова setRequestHeader
XMLHttpRequest
объекта. Спецификация CORS вызывает эти заголовки для создания запросов. Правило не применяется к заголовкам, которые браузер может задать, например User-Agent
, Host
или Content-Length
.
Ниже приведен пример ответа, аналогичного запросу предварительной проверки, сделанному на кнопке [Put test] в разделе Test CORS этого документа.
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
Предварительный запрос использует метод HTTP OPTIONS . Он может включать следующие заголовки:
- Метод Access-Control-Request-Method: метод HTTP, который будет использоваться для фактического запроса.
- Access-Control-Request-Headers: список заголовков запросов, которые приложение задает в фактическом запросе. Как упоминалось ранее, это не включает заголовки, которые задают браузеры, например
User-Agent
. - Методы access-Control-Allow-Methods
Если запрос на предварительную 200 OK
проверку запрещен, приложение возвращает ответ, но не задает заголовки CORS. Поэтому браузер не пытается выполнить запрос между источниками. Пример отклоненного предварительного запроса см. в разделе test CORS этого документа.
С помощью средств F12 консольное приложение отображает ошибку, аналогичную одной из следующих, в зависимости от браузера:
- Firefox: запрос между источниками заблокирован: та же политика источника запрещает чтение удаленного ресурса по адресу
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Причина: запрос CORS не выполнен). Подробнее - На основе Chromium: доступ к получению по адресу "https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5" из источникаhttps://cors3.azurewebsites.net "" был заблокирован политикой CORS: ответ на предварительный запрос не передает проверку контроля доступа: в запрошенном ресурсе отсутствует заголовок "Access-Control-Allow-Origin". Если этот непрозрачный ответ вам подходит, задайте для режима запроса значение "no-cors", чтобы извлечь ресурс с отключенным параметром CORS.
Чтобы разрешить определенные заголовки, вызовите 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();
Чтобы разрешить все заголовки запросов автора, вызовите 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();
Браузеры не согласованы в том, как они заданы Access-Control-Request-Headers
. Если одно из следующих вариантов:
- Заголовки имеют значение, отличное от других
"*"
- AllowAnyHeader вызывается: включите по крайней мере
Accept
,Content-Type
иOrigin
, а также все пользовательские заголовки, которые требуется поддерживать.
Код автоматического предварительного запроса
При применении политики CORS:
- Глобально путем вызова
app.UseCors
Program.cs
. - Использование атрибута
[EnableCors]
.
ASP.NET Core отвечает на запрос параметров предварительной проверки.
В разделе Test CORS этого документа показано это поведение.
Атрибут [HttpOptions] для предварительных запросов
Если CORS включена с соответствующей политикой, ASP.NET Core обычно реагирует на запросы предварительной проверки CORS автоматически.
Следующий код использует атрибут [HttpOptions] для создания конечных точек для запросов 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);
}
Сведения о тестировании предыдущего кода см. в разделе Test CORS с атрибутом [EnableCors] и методОм RequireCors.
Установка времени окончания срока действия предварительного срока действия
Заголовок Access-Control-Max-Age
указывает, сколько времени можно кэшировать ответ на предварительный запрос. Чтобы задать этот заголовок, вызовите 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();
Включение CORS в конечной точке
Как работает CORS
В этом разделе описывается, что происходит в запросе CORS на уровне HTTP-сообщений.
- CORS не является функцией безопасности. CORS — это стандарт W3C, позволяющий серверу расслабиться в политике одного источника.
- Например, злоумышленник может использовать межсайтовые скрипты (XSS) на вашем сайте и выполнить межсайтовый запрос на свой сайт с поддержкой CORS для кражи информации.
- API не безопаснее, разрешая CORS.
- Это до клиента (браузера) для принудительного применения CORS. Сервер выполняет запрос и возвращает ответ, он является клиентом, который возвращает ошибку и блокирует ответ. Например, любой из следующих средств отобразит ответ сервера:
- Fiddler
- HttpClient .NET
- Веб-браузер, введя URL-адрес в адресной строке.
- Это до клиента (браузера) для принудительного применения CORS. Сервер выполняет запрос и возвращает ответ, он является клиентом, который возвращает ошибку и блокирует ответ. Например, любой из следующих средств отобразит ответ сервера:
- Это способ для сервера, чтобы разрешить браузерам выполнять запрос XHR или API получения, который в противном случае будет запрещен.
- Браузеры без CORS не могут выполнять запросы между источниками. Перед CORS JSONP использовался для обхода этого ограничения. JSONP не использует XHR, он использует
<script>
тег для получения ответа. Скрипты могут загружаться между источниками.
- Браузеры без CORS не могут выполнять запросы между источниками. Перед CORS JSONP использовался для обхода этого ограничения. JSONP не использует XHR, он использует
Спецификация CORS представила несколько новых заголовков HTTP, которые позволяют выполнять запросы между источниками. Если браузер поддерживает CORS, он автоматически задает эти заголовки для запросов между источниками. Пользовательский код JavaScript не требуется для включения CORS.
Нажмите кнопку PUT теста в развернутом примере.
Заголовок Origin
:
- Предоставляет домен сайта, выполняющего запрос.
- Требуется и должен отличаться от узла.
Общие заголовки
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
Заголовки ответа
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
Заголовки запроса
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 ...
В OPTIONS
запросах сервер задает заголовок заголовков Access-Control-Allow-Origin: {allowed origin}
ответа в ответе. Например, в примере кода Delete [EnableCors]
запрос кнопки OPTIONS
содержит следующие заголовки:
Общие заголовки
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
Заголовки ответа
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
Заголовки запроса
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
В предыдущих заголовках ответа сервер задает заголовок Access-Control-Allow-Origin в ответе. Значение https://cors1.azurewebsites.net
этого заголовка соответствует заголовку Origin
из запроса.
Если AllowAnyOrigin вызывается, Access-Control-Allow-Origin: *
возвращается подстановочное значение. AllowAnyOrigin
разрешает любой источник.
Если ответ не включает Access-Control-Allow-Origin
заголовок, запрос между источниками завершается ошибкой. В частности, браузер запрещает запрос. Даже если сервер возвращает успешный ответ, браузер не делает ответ доступным для клиентского приложения.
Перенаправление HTTP на HTTPS вызывает ERR_INVALID_REDIRECT в предварительном запросе CORS
Запросы к конечной точке с использованием HTTP, перенаправленных на HTTPS, сбоем UseHttpsRedirection ERR_INVALID_REDIRECT on the CORS preflight request
.
Проекты API могут отклонять HTTP-запросы, а не использовать UseHttpsRedirection
для перенаправления запросов на HTTPS.
CORS в IIS
При развертывании в IIS CORS необходимо запустить перед проверкой подлинности Windows, если сервер не настроен для предоставления анонимного доступа. Для поддержки этого сценария необходимо установить и настроить модуль IIS CORS для приложения.
Тестирование CORS
Пример скачивания содержит код для тестирования CORS. См. раздел Практическое руководство. Скачивание файла. Примером является проект API с Razor добавленными страницами:
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();
Предупреждение
WithOrigins("https://localhost:<port>");
следует использовать только для тестирования примера приложения, аналогичного примеру кода скачивания.
ValuesController
Ниже приведены конечные точки для тестирования:
[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 предоставляется пакетом NuGet Rick.Docs.Samples.RouteInfo и отображает информацию о маршруте.
Протестируйте предыдущий пример кода с помощью одного из следующих подходов:
- Запустите пример с
dotnet run
использованием URL-адресаhttps://localhost:5001
по умолчанию. - Запустите пример из Visual Studio с портом 44398 для URL-адреса
https://localhost:44398
.
Использование браузера с инструментами F12:
Нажмите кнопку "Значения" и просмотрите заголовки на вкладке "Сеть".
Нажмите кнопку "Тест PUT ". Сведения о отображении запроса OPTIONS см. в разделе "Запросы ПАРАМЕТРОВ ". Тест PUT создает два запроса, предварительный запрос OPTIONS и запрос PUT.
Нажмите кнопку
GetValues2 [DisableCors]
, чтобы активировать неудачный запрос CORS. Как упоминалось в документе, ответ возвращает 200 успешно, но запрос CORS не выполняется. Перейдите на вкладку "Консоль", чтобы увидеть ошибку CORS. В зависимости от браузера отображается ошибка, аналогичная следующему:Доступ к получению
'https://cors1.azurewebsites.net/api/values/GetValues2'
из источника'https://cors3.azurewebsites.net'
заблокирован политикой CORS: в запрошенном ресурсе отсутствует заголовок Access-Control-Allow-Origin. Если этот непрозрачный ответ вам подходит, задайте для режима запроса значение "no-cors", чтобы извлечь ресурс с отключенным параметром CORS.
Конечные точки с поддержкой CORS можно протестировать с помощью средства, например curl или Fiddler. При использовании средства источник запроса, указанного Origin
заголовком, должен отличаться от узла, получающего запрос. Если запрос не является кросс-источником на основе значения заголовка Origin
:
- Для обработки запроса не требуется ПО промежуточного слоя CORS.
- Заголовки CORS не возвращаются в ответе.
Следующая команда используется curl
для выдачи запроса OPTIONS с информацией:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
Тестирование CORS с помощью атрибута [EnableCors] и метода RequireCors
Рассмотрим следующий код, который использует маршрутизацию конечных точек для включения CORS на основе каждой конечной точки с помощью 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();
Обратите внимание, что только /echo
конечная точка используется RequireCors
для разрешения запросов между источниками с помощью указанной политики. Приведенные ниже контроллеры позволяют CORS использовать атрибут [EnableCors].
TodoItems1Controller
Ниже приведены конечные точки для тестирования:
[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);
}
Кнопки Delete [EnableCors] и GET [EnableCors] успешно выполнены, так как конечные точки имеют [EnableCors]
и отвечают на предварительные запросы. Сбой других конечных точек. Кнопка GET завершается ошибкой, так как JavaScript отправляет:
headers: {
"Content-Type": "x-custom-header"
},
TodoItems2Controller
Ниже приведены аналогичные конечные точки, но содержит явный код для реагирования на запросы 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);
}
Приведенный выше код можно проверить, развернув пример, чтобы Azure.In раскрывающемся списке контроллера , выберите предварительный просмотр и установите контроллер. Все вызовы CORS к TodoItems2Controller
конечным точкам успешно выполнены.
Дополнительные ресурсы
Авторы: Рик Андерсон (Rick Anderson) и Кирк Ларкин (Kirk Larkin)
В этой статье показано, как включить CORS в приложении ASP.NET Core.
Система безопасности браузера предотвращает запросы веб-страницы к другому домену, отличному от того, который обслуживает веб-страницу. Это ограничение называется политика одного источника. Эта политика предотвращает чтение вредоносным сайтом конфиденциальных данных с другого сайта. Иногда вы должны разрешить другим сайтам выполнять запросы к приложению независимо от источника. Дополнительные сведения см. в статье Mozilla CORS.
Общий доступ к ресурсам между источниками (CORS):
- Является стандартом W3C, который позволяет серверу расслабиться в политике одного и того же источника.
- Не является функцией безопасности, CORS смягчает безопасность. API не безопаснее, разрешая CORS. Дополнительные сведения см. в статье о работе CORS.
- Позволяет серверу явно разрешать некоторые запросы между источниками при отклонении других.
- Является более безопасным и более гибким, чем более ранние методы, такие как JSONP.
Просмотреть или скачать образец кода (описание загрузки)
Тот же источник
Два URL-адреса имеют одинаковый источник, если они имеют одинаковые схемы, узлы и порты (RFC 6454).
Эти два URL-адреса имеют одинаковый источник:
https://example.com/foo.html
https://example.com/bar.html
Эти URL-адреса имеют разные источники, отличные от предыдущих двух URL-адресов:
https://example.net
: другой доменhttps://www.example.com/foo.html
: другой поддоменhttp://example.com/foo.html
: другая схемаhttps://example.com:9000/foo.html
: другой порт
Включение CORS
Существует три способа включения CORS:
- В ПО промежуточного слоя с помощью именованной политики или политики по умолчанию.
- Использование маршрутизации конечных точек.
- С атрибутом [EnableCors] .
Использование атрибута [EnableCors] с именованной политикой обеспечивает лучший контроль в ограничении конечных точек, поддерживающих CORS.
Предупреждение
UseCors должен вызываться в правильном порядке. Дополнительные сведения см. в порядке по промежуточного слоя. Например, UseCors
необходимо вызвать перед UseResponseCaching использованием UseResponseCaching
.
Каждый подход подробно описан в следующих разделах.
CORS с именованной политикой и ПО промежуточного слоя
ПО промежуточного слоя CORS обрабатывает запросы между источниками. Следующий код применяет политику CORS ко всем конечным точкам приложения с указанными источниками:
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();
Предыдущий код:
- Задает для
_myAllowSpecificOrigins
имени политики значение . Имя политики является произвольным. - UseCors Вызывает метод расширения и задает
_myAllowSpecificOrigins
политику CORS.UseCors
добавляет ПО промежуточного слоя CORS.UseCors
Вызов должен быть помещен послеUseRouting
, но доUseAuthorization
. Дополнительные сведения см. в порядке по промежуточного слоя. - Вызовы AddCors с лямбда-выражением. Лямбда принимает CorsPolicyBuilder объект. Параметры конфигурации, например
WithOrigins
, описаны далее в этой статье. _myAllowSpecificOrigins
Включает политику CORS для всех конечных точек контроллера. См. маршрутизацию конечных точек, чтобы применить политику CORS к определенным конечным точкам.- При использовании по промежуточного слоя кэширования ответов вызовите UseCors раньше UseResponseCaching.
При маршрутизации конечных точек ПО промежуточного слоя CORS необходимо настроить для выполнения между вызовами UseRouting
и UseEndpoints
.
Вызов AddCors метода добавляет службы CORS в контейнер службы приложения:
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();
Дополнительные сведения см. в разделе "Параметры политики CORS" в этом документе.
Методы CorsPolicyBuilder можно связать, как показано в следующем коде:
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();
Примечание. Указанный URL-адрес не должен содержать косую черту (/
). Если URL-адрес завершается /
, сравнение возвращается false
и заголовок не возвращается.
Предупреждение
UseCors
должно быть помещено после UseRouting
и до UseAuthorization
. Это позволяет убедиться, что заголовки CORS включены в ответ как для авторизованных, так и для несанкционированных вызовов.
Порядок UseCors и UseStaticFiles
Как правило, UseStaticFiles
вызывается раньше UseCors
. Приложения, использующие JavaScript для получения статических файлов, должны вызываться перед UseStaticFiles
вызовомUseCors
.
CORS с политикой по умолчанию и ПО промежуточного слоя
Следующий выделенный код включает политику CORS по умолчанию:
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();
Приведенный выше код применяет политику CORS по умолчанию ко всем конечным точкам контроллера.
Включение CORS с маршрутизацией конечных точек
Включение CORS на основе каждой конечной точки не RequireCors
поддерживает автоматические предварительные запросы. Дополнительные сведения см. в этой проблеме GitHub и тестировании CORS с маршрутизацией конечных точек и [HttpOptions].
При маршрутизации конечных точек CORS можно включить на основе каждой конечной точки с помощью RequireCors набора методов расширения:
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();
В предыдущем коде:
app.UseCors
включает ПО промежуточного слоя CORS. Так как политика по умолчанию не настроена,app.UseCors()
только не включает CORS.- Конечные
/echo
точки и конечные точки контроллера разрешают запросы между источниками с помощью указанной политики. - Конечные
/echo2
точки и Razor страницы не разрешают запросы между источниками, так как политика по умолчанию не указана.
Атрибут [DisableCors] не отключает CORS, включенную маршрутизацией конечных точек.RequireCors
Ознакомьтесь с инструкциями по тестированию CORS с маршрутизацией конечных точек и [HttpOptions] , как показано выше.
Включение CORS с атрибутами
Включение CORS с атрибутом [EnableCors] и применение именованной политики только к тем конечным точкам, которым требуется CORS, обеспечивает лучший контроль.
Атрибут [EnableCors] предоставляет альтернативу применению CORS глобально. Атрибут [EnableCors]
включает CORS для выбранных конечных точек, а не для всех конечных точек:
[EnableCors]
указывает политику по умолчанию.[EnableCors("{Policy String}")]
указывает именованную политику.
Атрибут [EnableCors]
можно применить к:
- Razor Страница
PageModel
- Контроллер
- Метод действия контроллера
Различные политики можно применять к контроллерам, моделям страниц или методам действий с атрибутом [EnableCors]
. [EnableCors]
Если атрибут применяется к контроллеру, модели страницы или методу действия, а CORS включен в ПО промежуточного слоя, применяются обе политики. Рекомендуется объединять политики. Используйте[EnableCors]
атрибут или ПО промежуточного слоя, а не в одном приложении.
Следующий код применяет другую политику к каждому методу:
[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(),
};
}
}
Следующий код создает две политики 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();
Для лучшего контроля ограничения запросов CORS:
- Используется
[EnableCors("MyPolicy")]
с именованной политикой. - Не определяйте политику по умолчанию.
- Не используйте маршрутизацию конечных точек.
Код в следующем разделе соответствует предыдущему списку.
Отключение CORS
Атрибут [DisableCors] не отключает CORS, включенную маршрутизацией конечных точек.
Следующий код определяет политику "MyPolicy"
CORS:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.MapRazorPages();
app.Run();
Следующий код отключает CORS для 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();
}
Предыдущий код:
- Не включает CORS с маршрутизацией конечных точек.
- Не определяет политику CORS по умолчанию.
- Использует [EnableCors("MyPolicy")] для включения
"MyPolicy"
политики CORS для контроллера. - Отключает CORS для
GetValues2
метода.
Сведения о тестировании предыдущего кода см. в разделе Test CORS .
Параметры политики CORS
В этом разделе описаны различные параметры, которые можно задать в политике CORS:
- Настройка разрешенных источников
- Установка разрешенных методов HTTP
- Настройка заголовков разрешенных запросов
- Настройка заголовков ответов, предоставляемых
- Учетные данные в запросах между источниками
- Установка времени окончания срока действия предварительного срока действия
AddPolicy вызывается в Program.cs
. Для некоторых вариантов может быть полезно сначала ознакомиться с разделом о работе CORS.
Настройка разрешенных источников
AllowAnyOrigin: разрешает запросы CORS из всех источников с любой схемой (http
или https
). AllowAnyOrigin
небезопасно, так как любой веб-сайт может выполнять запросы между источниками приложения.
Примечание.
Указание AllowAnyOrigin
и AllowCredentials
является небезопасной конфигурацией и может привести к подделке межсайтовых запросов. Служба CORS возвращает недопустимый ответ CORS, если приложение настроено с использованием обоих методов.
AllowAnyOrigin
влияет на предварительные Access-Control-Allow-Origin
запросы и заголовок. Дополнительные сведения см. в разделе "Предварительные запросы ".
SetIsOriginAllowedToAllowWildcardSubdomains: задает IsOriginAllowed свойство политики, которая позволяет источникам сопоставлять настроенный домен подстановочных знаков при оценке допустимости источника.
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();
Установка разрешенных методов HTTP
- Разрешает любой метод HTTP:
- Влияет на предварительные
Access-Control-Allow-Methods
запросы и заголовок. Дополнительные сведения см. в разделе "Предварительные запросы ".
Настройка заголовков разрешенных запросов
Чтобы разрешить отправку определенных заголовков в запросе CORS, называется заголовками запросов автора, вызовом WithHeaders и указанием разрешенных заголовков:
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();
Чтобы разрешить все заголовки запросов автора, вызовите 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
влияет на предварительные запросы и заголовок Access-Control-Request-Headers . Дополнительные сведения см. в разделе "Предварительные запросы ".
Политика ПО промежуточного слоя CORS соответствует определенным заголовкам, указанным WithHeaders
только в том случае, если заголовки, отправленные точно Access-Control-Request-Headers
соответствуют заголовкам, указанным в .WithHeaders
Например, рассмотрим приложение, настроенное следующим образом:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
ПО промежуточного слоя CORS отклоняет предварительный запрос со следующим заголовком запроса, так как Content-Language
(HeaderNames.ContentLanguage) не указан вWithHeaders
:
Access-Control-Request-Headers: Cache-Control, Content-Language
Приложение возвращает ответ 200 OK , но не отправляет заголовки CORS обратно. Поэтому браузер не пытается выполнить запрос между источниками.
Настройка заголовков ответов, предоставляемых
По умолчанию браузер не предоставляет все заголовки ответа приложению. Дополнительные сведения см. в разделе "Общий доступ к ресурсам между источниками W3C" (терминология): простой заголовок ответа.
Заголовки ответов, доступные по умолчанию:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
Спецификация CORS вызывает эти заголовки простых заголовков ответа. Чтобы сделать другие заголовки доступными для приложения, вызовите 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();
Учетные данные в запросах между источниками
Учетные данные требуют специальной обработки в запросе CORS. По умолчанию браузер не отправляет учетные данные с запросом между источниками. Учетные данные включают файлы cookie и схемы проверки подлинности HTTP. Чтобы отправить учетные данные с запросом между источниками, клиент должен задать значение XMLHttpRequest.withCredentials
true
.
Использование XMLHttpRequest
напрямую:
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
Использование jQuery:
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
Использование API получения:
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
Сервер должен разрешить учетные данные. Чтобы разрешить учетные данные между источниками, вызовите 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();
Http-ответ содержит заголовок, который сообщает браузеру Access-Control-Allow-Credentials
, что сервер разрешает учетные данные для запроса между источниками.
Если браузер отправляет учетные данные, но ответ не содержит допустимый Access-Control-Allow-Credentials
заголовок, браузер не предоставляет ответ приложению, а запрос между источниками завершается ошибкой.
Разрешение учетных данных между источниками — это риск безопасности. Веб-сайт в другом домене может отправлять учетные данные пользователя, выполнившего вход, в приложение от имени пользователя без знаний пользователя.
Спецификация CORS также указывает, что установка источников "*"
(всех источников) недопустима, если заголовок Access-Control-Allow-Credentials
присутствует.
Предварительные запросы
Для некоторых запросов CORS браузер отправляет дополнительный запрос OPTIONS перед выполнением фактического запроса. Этот запрос называется предварительным запросом. Браузер может пропустить предварительный запрос, если выполняются все следующие условия:
- Метод запроса — GET, HEAD или POST.
- Приложение не задает заголовки запросов, отличные от
Accept
,Accept-Language
, ,Content-Language
Content-Type
илиLast-Event-ID
. - Заголовок
Content-Type
, если задано, имеет одно из следующих значений:application/x-www-form-urlencoded
multipart/form-data
text/plain
Правило заголовков запросов, заданных для клиентского запроса, применяется к заголовкам, заданным приложением путем вызова setRequestHeader
XMLHttpRequest
объекта. Спецификация CORS вызывает эти заголовки для создания запросов. Правило не применяется к заголовкам, которые браузер может задать, например User-Agent
, Host
или Content-Length
.
Ниже приведен пример ответа, аналогичного запросу предварительной проверки, сделанному на кнопке [Put test] в разделе Test CORS этого документа.
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
Предварительный запрос использует метод HTTP OPTIONS . Он может включать следующие заголовки:
- Метод Access-Control-Request-Method: метод HTTP, который будет использоваться для фактического запроса.
- Access-Control-Request-Headers: список заголовков запросов, которые приложение задает в фактическом запросе. Как упоминалось ранее, это не включает заголовки, которые задают браузеры, например
User-Agent
. - Методы access-Control-Allow-Methods
Если запрос на предварительную 200 OK
проверку запрещен, приложение возвращает ответ, но не задает заголовки CORS. Поэтому браузер не пытается выполнить запрос между источниками. Пример отклоненного предварительного запроса см. в разделе test CORS этого документа.
С помощью средств F12 консольное приложение отображает ошибку, аналогичную одной из следующих, в зависимости от браузера:
- Firefox: запрос между источниками заблокирован: та же политика источника запрещает чтение удаленного ресурса по адресу
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Причина: запрос CORS не выполнен). Подробнее - На основе Chromium: доступ к получению по адресу "https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5" из источникаhttps://cors3.azurewebsites.net "" был заблокирован политикой CORS: ответ на предварительный запрос не передает проверку контроля доступа: в запрошенном ресурсе отсутствует заголовок "Access-Control-Allow-Origin". Если этот непрозрачный ответ вам подходит, задайте для режима запроса значение "no-cors", чтобы извлечь ресурс с отключенным параметром CORS.
Чтобы разрешить определенные заголовки, вызовите 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();
Чтобы разрешить все заголовки запросов автора, вызовите 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();
Браузеры не согласованы в том, как они заданы Access-Control-Request-Headers
. Если одно из следующих вариантов:
- Заголовки имеют значение, отличное от других
"*"
- AllowAnyHeader вызывается: включите по крайней мере
Accept
,Content-Type
иOrigin
, а также все пользовательские заголовки, которые требуется поддерживать.
Код автоматического предварительного запроса
При применении политики CORS:
- Глобально путем вызова
app.UseCors
Program.cs
. - Использование атрибута
[EnableCors]
.
ASP.NET Core отвечает на запрос параметров предварительной проверки.
Включение CORS на основе каждой конечной точки с использованием RequireCors
в настоящее время не поддерживает автоматические предварительные запросы.
В разделе Test CORS этого документа показано это поведение.
Атрибут [HttpOptions] для предварительных запросов
Если CORS включена с соответствующей политикой, ASP.NET Core обычно реагирует на запросы предварительной проверки CORS автоматически. В некоторых сценариях это может быть не так. Например, использование CORS с маршрутизацией конечных точек.
Следующий код использует атрибут [HttpOptions] для создания конечных точек для запросов 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);
}
Инструкции по тестированию предыдущего кода см. в разделе Test CORS с маршрутизацией конечных точек и [HttpOptions] .
Установка времени окончания срока действия предварительного срока действия
Заголовок Access-Control-Max-Age
указывает, сколько времени можно кэшировать ответ на предварительный запрос. Чтобы задать этот заголовок, вызовите 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();
Как работает CORS
В этом разделе описывается, что происходит в запросе CORS на уровне HTTP-сообщений.
- CORS не является функцией безопасности. CORS — это стандарт W3C, позволяющий серверу расслабиться в политике одного источника.
- Например, злоумышленник может использовать межсайтовые скрипты (XSS) на вашем сайте и выполнить межсайтовый запрос на свой сайт с поддержкой CORS для кражи информации.
- API не безопаснее, разрешая CORS.
- Это до клиента (браузера) для принудительного применения CORS. Сервер выполняет запрос и возвращает ответ, он является клиентом, который возвращает ошибку и блокирует ответ. Например, любой из следующих средств отобразит ответ сервера:
- Fiddler
- HttpClient .NET
- Веб-браузер, введя URL-адрес в адресной строке.
- Это до клиента (браузера) для принудительного применения CORS. Сервер выполняет запрос и возвращает ответ, он является клиентом, который возвращает ошибку и блокирует ответ. Например, любой из следующих средств отобразит ответ сервера:
- Это способ для сервера, чтобы разрешить браузерам выполнять запрос XHR или API получения, который в противном случае будет запрещен.
- Браузеры без CORS не могут выполнять запросы между источниками. Перед CORS JSONP использовался для обхода этого ограничения. JSONP не использует XHR, он использует
<script>
тег для получения ответа. Скрипты могут загружаться между источниками.
- Браузеры без CORS не могут выполнять запросы между источниками. Перед CORS JSONP использовался для обхода этого ограничения. JSONP не использует XHR, он использует
Спецификация CORS представила несколько новых заголовков HTTP, которые позволяют выполнять запросы между источниками. Если браузер поддерживает CORS, он автоматически задает эти заголовки для запросов между источниками. Пользовательский код JavaScript не требуется для включения CORS.
Ниже приведен пример запроса между источниками из кнопки "Тест значений " в https://cors1.azurewebsites.net/api/values
. Заголовок Origin
:
- Предоставляет домен сайта, выполняющего запрос.
- Требуется и должен отличаться от узла.
Общие заголовки
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
Заголовки ответа
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
Заголовки запроса
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 ...
В OPTIONS
запросах сервер задает заголовок заголовков Access-Control-Allow-Origin: {allowed origin}
ответа в ответе. Например, развернутый пример запроса кнопки OPTIONS
Delete [EnableCors] содержит следующие заголовки:
Общие заголовки
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
Заголовки ответа
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
Заголовки запроса
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
В предыдущих заголовках ответа сервер задает заголовок Access-Control-Allow-Origin в ответе. Значение https://cors1.azurewebsites.net
этого заголовка соответствует заголовку Origin
из запроса.
Если AllowAnyOrigin вызывается, Access-Control-Allow-Origin: *
возвращается подстановочное значение. AllowAnyOrigin
разрешает любой источник.
Если ответ не включает Access-Control-Allow-Origin
заголовок, запрос между источниками завершается ошибкой. В частности, браузер запрещает запрос. Даже если сервер возвращает успешный ответ, браузер не делает ответ доступным для клиентского приложения.
Перенаправление HTTP на HTTPS вызывает ERR_INVALID_REDIRECT в предварительном запросе CORS
Запросы к конечной точке с использованием HTTP, перенаправленных на HTTPS, сбоем UseHttpsRedirection ERR_INVALID_REDIRECT on the CORS preflight request
.
Проекты API могут отклонять HTTP-запросы, а не использовать UseHttpsRedirection
для перенаправления запросов на HTTPS.
Отображение запросов OPTIONS
По умолчанию браузеры Chrome и Edge не отображают запросы OPTIONS на сетевой вкладке инструментов F12. Чтобы отобразить запросы OPTIONS в этих браузерах, выполните следующие действия.
chrome://flags/#out-of-blink-cors
илиedge://flags/#out-of-blink-cors
- отключите флаг.
- перезапуск.
Firefox отображает запросы OPTIONS по умолчанию.
CORS в IIS
При развертывании в IIS CORS необходимо запустить перед проверкой подлинности Windows, если сервер не настроен для предоставления анонимного доступа. Для поддержки этого сценария необходимо установить и настроить модуль IIS CORS для приложения.
Тестирование CORS
Пример скачивания содержит код для тестирования CORS. См. раздел Практическое руководство. Скачивание файла. Примером является проект API с Razor добавленными страницами:
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();
Предупреждение
WithOrigins("https://localhost:<port>");
следует использовать только для тестирования примера приложения, аналогичного примеру кода скачивания.
ValuesController
Ниже приведены конечные точки для тестирования:
[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 предоставляется пакетом NuGet Rick.Docs.Samples.RouteInfo и отображает информацию о маршруте.
Протестируйте предыдущий пример кода с помощью одного из следующих подходов:
- Запустите пример с
dotnet run
использованием URL-адресаhttps://localhost:5001
по умолчанию. - Запустите пример из Visual Studio с портом 44398 для URL-адреса
https://localhost:44398
.
Использование браузера с инструментами F12:
Нажмите кнопку "Значения" и просмотрите заголовки на вкладке "Сеть".
Нажмите кнопку "Тест PUT ". Сведения о отображении запроса OPTIONS см. в разделе "Запросы ПАРАМЕТРОВ ". Тест PUT создает два запроса, предварительный запрос OPTIONS и запрос PUT.
Нажмите кнопку
GetValues2 [DisableCors]
, чтобы активировать неудачный запрос CORS. Как упоминалось в документе, ответ возвращает 200 успешно, но запрос CORS не выполняется. Перейдите на вкладку "Консоль", чтобы увидеть ошибку CORS. В зависимости от браузера отображается ошибка, аналогичная следующему:Доступ к получению
'https://cors1.azurewebsites.net/api/values/GetValues2'
из источника'https://cors3.azurewebsites.net'
заблокирован политикой CORS: в запрошенном ресурсе отсутствует заголовок Access-Control-Allow-Origin. Если этот непрозрачный ответ вам подходит, задайте для режима запроса значение "no-cors", чтобы извлечь ресурс с отключенным параметром CORS.
Конечные точки с поддержкой CORS можно протестировать с помощью средства, например curl или Fiddler. При использовании средства источник запроса, указанного Origin
заголовком, должен отличаться от узла, получающего запрос. Если запрос не является кросс-источником на основе значения заголовка Origin
:
- Для обработки запроса не требуется ПО промежуточного слоя CORS.
- Заголовки CORS не возвращаются в ответе.
Следующая команда используется curl
для выдачи запроса OPTIONS с информацией:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
Тестирование CORS с помощью маршрутизации конечных точек и [HttpOptions]
Включение CORS на основе каждой конечной точки с использованием RequireCors
в настоящее время не поддерживает автоматические предварительные запросы. Рассмотрим следующий код, использующий маршрутизацию конечных точек для включения 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();
TodoItems1Controller
Ниже приведены конечные точки для тестирования:
[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);
}
Проверьте предыдущий код на тестовой странице (https://cors1.azurewebsites.net/test?number=1
) развернутого примера.
Кнопки Delete [EnableCors] и GET [EnableCors] успешно выполнены, так как конечные точки имеют [EnableCors]
и отвечают на предварительные запросы. Сбой других конечных точек. Кнопка GET завершается ошибкой, так как JavaScript отправляет:
headers: {
"Content-Type": "x-custom-header"
},
TodoItems2Controller
Ниже приведены аналогичные конечные точки, но содержит явный код для реагирования на запросы 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);
}
Приведенный выше код можно проверить, развернув пример, чтобы Azure.In раскрывающемся списке контроллера , выберите предварительный просмотр и установите контроллер. Все вызовы CORS к TodoItems2Controller
конечным точкам успешно выполнены.
Дополнительные ресурсы
Авторы: Рик Андерсон (Rick Anderson) и Кирк Ларкин (Kirk Larkin)
В этой статье показано, как включить CORS в приложении ASP.NET Core.
Система безопасности браузера предотвращает запросы веб-страницы к другому домену, отличному от того, который обслуживает веб-страницу. Это ограничение называется политика одного источника. Эта политика предотвращает чтение вредоносным сайтом конфиденциальных данных с другого сайта. Иногда вы должны разрешить другим сайтам выполнять запросы к приложению независимо от источника. Дополнительные сведения см. в статье Mozilla CORS.
Общий доступ к ресурсам между источниками (CORS):
- Является стандартом W3C, который позволяет серверу расслабиться в политике одного и того же источника.
- Не является функцией безопасности, CORS смягчает безопасность. API не безопаснее, разрешая CORS. Дополнительные сведения см. в статье о работе CORS.
- Позволяет серверу явно разрешать некоторые запросы между источниками при отклонении других.
- Является более безопасным и более гибким, чем более ранние методы, такие как JSONP.
Просмотреть или скачать образец кода (описание загрузки)
Тот же источник
Два URL-адреса имеют одинаковый источник, если они имеют одинаковые схемы, узлы и порты (RFC 6454).
Эти два URL-адреса имеют одинаковый источник:
https://example.com/foo.html
https://example.com/bar.html
Эти URL-адреса имеют разные источники, отличные от предыдущих двух URL-адресов:
https://example.net
: другой доменhttps://www.example.com/foo.html
: другой поддоменhttp://example.com/foo.html
: другая схемаhttps://example.com:9000/foo.html
: другой порт
Включение CORS
Существует три способа включения CORS:
- В ПО промежуточного слоя с помощью именованной политики или политики по умолчанию.
- Использование маршрутизации конечных точек.
- С атрибутом [EnableCors] .
Использование атрибута [EnableCors] с именованной политикой обеспечивает лучший контроль в ограничении конечных точек, поддерживающих CORS.
Предупреждение
UseCors должен вызываться в правильном порядке. Дополнительные сведения см. в порядке по промежуточного слоя. Например, UseCors
необходимо вызвать перед UseResponseCaching использованием UseResponseCaching
.
Каждый подход подробно описан в следующих разделах.
CORS с именованной политикой и ПО промежуточного слоя
ПО промежуточного слоя CORS обрабатывает запросы между источниками. Следующий код применяет политику CORS ко всем конечным точкам приложения с указанными источниками:
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();
});
}
}
Предыдущий код:
- Задает для
_myAllowSpecificOrigins
имени политики значение . Имя политики является произвольным. - UseCors Вызывает метод расширения и задает
_myAllowSpecificOrigins
политику CORS.UseCors
добавляет ПО промежуточного слоя CORS.UseCors
Вызов должен быть помещен послеUseRouting
, но доUseAuthorization
. Дополнительные сведения см. в порядке по промежуточного слоя. - Вызовы AddCors с лямбда-выражением. Лямбда принимает CorsPolicyBuilder объект. Параметры конфигурации, например
WithOrigins
, описаны далее в этой статье. _myAllowSpecificOrigins
Включает политику CORS для всех конечных точек контроллера. См. маршрутизацию конечных точек, чтобы применить политику CORS к определенным конечным точкам.- При использовании по промежуточного слоя кэширования ответов вызовите UseCors раньше UseResponseCaching.
При маршрутизации конечных точек ПО промежуточного слоя CORS необходимо настроить для выполнения между вызовами UseRouting
и UseEndpoints
.
Ознакомьтесь с инструкциями по тестированию кода, аналогичного приведенному выше.
Вызов AddCors метода добавляет службы CORS в контейнер службы приложения:
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();
}
Дополнительные сведения см. в разделе "Параметры политики CORS" в этом документе.
Методы CorsPolicyBuilder можно связать, как показано в следующем коде:
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();
}
Примечание. Указанный URL-адрес не должен содержать косую черту (/
). Если URL-адрес завершается /
, сравнение возвращается false
и заголовок не возвращается.
CORS с политикой по умолчанию и ПО промежуточного слоя
Следующий выделенный код включает политику CORS по умолчанию:
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();
});
}
}
Приведенный выше код применяет политику CORS по умолчанию ко всем конечным точкам контроллера.
Включение CORS с маршрутизацией конечных точек
Включение CORS на основе каждой конечной точки не RequireCors
поддерживает автоматические предварительные запросы. Дополнительные сведения см. в этой проблеме GitHub и тестировании CORS с маршрутизацией конечных точек и [HttpOptions].
При маршрутизации конечных точек CORS можно включить на основе каждой конечной точки с помощью RequireCors набора методов расширения:
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();
});
}
}
В предыдущем коде:
app.UseCors
включает ПО промежуточного слоя CORS. Так как политика по умолчанию не настроена,app.UseCors()
только не включает CORS.- Конечные
/echo
точки и конечные точки контроллера разрешают запросы между источниками с помощью указанной политики. - Конечные
/echo2
точки и Razor страницы не разрешают запросы между источниками, так как политика по умолчанию не указана.
Атрибут [DisableCors] не отключает CORS, включенную маршрутизацией конечных точек.RequireCors
Ознакомьтесь с инструкциями по тестированию CORS с маршрутизацией конечных точек и [HttpOptions] , как показано выше.
Включение CORS с атрибутами
Включение CORS с атрибутом [EnableCors] и применение именованной политики только к тем конечным точкам, которым требуется CORS, обеспечивает лучший контроль.
Атрибут [EnableCors] предоставляет альтернативу применению CORS глобально. Атрибут [EnableCors]
включает CORS для выбранных конечных точек, а не для всех конечных точек:
[EnableCors]
указывает политику по умолчанию.[EnableCors("{Policy String}")]
указывает именованную политику.
Атрибут [EnableCors]
можно применить к:
- Razor Страница
PageModel
- Контроллер
- Метод действия контроллера
Различные политики можно применять к контроллерам, моделям страниц или методам действий с атрибутом [EnableCors]
. [EnableCors]
Если атрибут применяется к контроллеру, модели страницы или методу действия, а CORS включен в ПО промежуточного слоя, применяются обе политики. Рекомендуется объединять политики. Используйте[EnableCors]
атрибут или ПО промежуточного слоя, а не в одном приложении.
Следующий код применяет другую политику к каждому методу:
[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(),
};
}
}
Следующий код создает две политики 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();
});
}
}
Для лучшего контроля ограничения запросов CORS:
- Используется
[EnableCors("MyPolicy")]
с именованной политикой. - Не определяйте политику по умолчанию.
- Не используйте маршрутизацию конечных точек.
Код в следующем разделе соответствует предыдущему списку.
Ознакомьтесь с инструкциями по тестированию кода, аналогичного приведенному выше.
Отключение CORS
Атрибут [DisableCors] не отключает CORS, включенную маршрутизацией конечных точек.
Следующий код определяет политику "MyPolicy"
CORS:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.WithMethods("PUT", "DELETE", "GET");
});
});
services.AddControllers();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapRazorPages();
});
}
}
Следующий код отключает CORS для 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();
}
Предыдущий код:
- Не включает CORS с маршрутизацией конечных точек.
- Не определяет политику CORS по умолчанию.
- Использует [EnableCors("MyPolicy")] для включения
"MyPolicy"
политики CORS для контроллера. - Отключает CORS для
GetValues2
метода.
Сведения о тестировании предыдущего кода см. в разделе Test CORS .
Параметры политики CORS
В этом разделе описаны различные параметры, которые можно задать в политике CORS:
- Настройка разрешенных источников
- Установка разрешенных методов HTTP
- Настройка заголовков разрешенных запросов
- Настройка заголовков ответов, предоставляемых
- Учетные данные в запросах между источниками
- Установка времени окончания срока действия предварительного срока действия
AddPolicy вызывается в Startup.ConfigureServices
. Для некоторых вариантов может быть полезно сначала ознакомиться с разделом о работе CORS.
Настройка разрешенных источников
AllowAnyOrigin: разрешает запросы CORS из всех источников с любой схемой (http
или https
). AllowAnyOrigin
небезопасно, так как любой веб-сайт может выполнять запросы между источниками приложения.
Примечание.
Указание AllowAnyOrigin
и AllowCredentials
является небезопасной конфигурацией и может привести к подделке межсайтовых запросов. Служба CORS возвращает недопустимый ответ CORS, если приложение настроено с использованием обоих методов.
AllowAnyOrigin
влияет на предварительные Access-Control-Allow-Origin
запросы и заголовок. Дополнительные сведения см. в разделе "Предварительные запросы ".
SetIsOriginAllowedToAllowWildcardSubdomains: задает IsOriginAllowed свойство политики, которая позволяет источникам сопоставлять настроенный домен подстановочных знаков при оценке допустимости источника.
options.AddPolicy("MyAllowSubdomainPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
Установка разрешенных методов HTTP
- Разрешает любой метод HTTP:
- Влияет на предварительные
Access-Control-Allow-Methods
запросы и заголовок. Дополнительные сведения см. в разделе "Предварительные запросы ".
Настройка заголовков разрешенных запросов
Чтобы разрешить отправку определенных заголовков в запросе CORS, называется заголовками запросов автора, вызовом WithHeaders и указанием разрешенных заголовков:
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
// requires using Microsoft.Net.Http.Headers;
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
Чтобы разрешить все заголовки запросов автора, вызовите AllowAnyHeader:
options.AddPolicy("MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
AllowAnyHeader
влияет на предварительные запросы и заголовок Access-Control-Request-Headers . Дополнительные сведения см. в разделе "Предварительные запросы ".
Политика ПО промежуточного слоя CORS соответствует определенным заголовкам, указанным WithHeaders
только в том случае, если заголовки, отправленные точно Access-Control-Request-Headers
соответствуют заголовкам, указанным в .WithHeaders
Например, рассмотрим приложение, настроенное следующим образом:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
ПО промежуточного слоя CORS отклоняет предварительный запрос со следующим заголовком запроса, так как Content-Language
(HeaderNames.ContentLanguage) не указан вWithHeaders
:
Access-Control-Request-Headers: Cache-Control, Content-Language
Приложение возвращает ответ 200 OK , но не отправляет заголовки CORS обратно. Поэтому браузер не пытается выполнить запрос между источниками.
Настройка заголовков ответов, предоставляемых
По умолчанию браузер не предоставляет все заголовки ответа приложению. Дополнительные сведения см. в разделе "Общий доступ к ресурсам между источниками W3C" (терминология): простой заголовок ответа.
Заголовки ответов, доступные по умолчанию:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
Спецификация CORS вызывает эти заголовки простых заголовков ответа. Чтобы сделать другие заголовки доступными для приложения, вызовите WithExposedHeaders:
options.AddPolicy("MyExposeResponseHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.WithExposedHeaders("x-custom-header");
});
Учетные данные в запросах между источниками
Учетные данные требуют специальной обработки в запросе CORS. По умолчанию браузер не отправляет учетные данные с запросом между источниками. Учетные данные включают файлы cookie и схемы проверки подлинности HTTP. Чтобы отправить учетные данные с запросом между источниками, клиент должен задать значение XMLHttpRequest.withCredentials
true
.
Использование XMLHttpRequest
напрямую:
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
Использование jQuery:
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
Использование API получения:
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
Сервер должен разрешить учетные данные. Чтобы разрешить учетные данные между источниками, вызовите AllowCredentials:
options.AddPolicy("MyMyAllowCredentialsPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.AllowCredentials();
});
Http-ответ содержит заголовок, который сообщает браузеру Access-Control-Allow-Credentials
, что сервер разрешает учетные данные для запроса между источниками.
Если браузер отправляет учетные данные, но ответ не содержит допустимый Access-Control-Allow-Credentials
заголовок, браузер не предоставляет ответ приложению, а запрос между источниками завершается ошибкой.
Разрешение учетных данных между источниками — это риск безопасности. Веб-сайт в другом домене может отправлять учетные данные пользователя, выполнившего вход, в приложение от имени пользователя без знаний пользователя.
Спецификация CORS также указывает, что установка источников "*"
(всех источников) недопустима, если заголовок Access-Control-Allow-Credentials
присутствует.
Предварительные запросы
Для некоторых запросов CORS браузер отправляет дополнительный запрос OPTIONS перед выполнением фактического запроса. Этот запрос называется предварительным запросом. Браузер может пропустить предварительный запрос, если выполняются все следующие условия:
- Метод запроса — GET, HEAD или POST.
- Приложение не задает заголовки запросов, отличные от
Accept
,Accept-Language
, ,Content-Language
Content-Type
илиLast-Event-ID
. - Заголовок
Content-Type
, если задано, имеет одно из следующих значений:application/x-www-form-urlencoded
multipart/form-data
text/plain
Правило заголовков запросов, заданных для клиентского запроса, применяется к заголовкам, заданным приложением путем вызова setRequestHeader
XMLHttpRequest
объекта. Спецификация CORS вызывает эти заголовки для создания запросов. Правило не применяется к заголовкам, которые браузер может задать, например User-Agent
, Host
или Content-Length
.
Ниже приведен пример ответа, аналогичного запросу предварительной проверки, сделанному на кнопке [Put test] в разделе Test CORS этого документа.
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
Предварительный запрос использует метод HTTP OPTIONS . Он может включать следующие заголовки:
- Метод Access-Control-Request-Method: метод HTTP, который будет использоваться для фактического запроса.
- Access-Control-Request-Headers: список заголовков запросов, которые приложение задает в фактическом запросе. Как упоминалось ранее, это не включает заголовки, которые задают браузеры, например
User-Agent
. - Методы access-Control-Allow-Methods
Если запрос на предварительную 200 OK
проверку запрещен, приложение возвращает ответ, но не задает заголовки CORS. Поэтому браузер не пытается выполнить запрос между источниками. Пример отклоненного предварительного запроса см. в разделе test CORS этого документа.
С помощью средств F12 консольное приложение отображает ошибку, аналогичную одной из следующих, в зависимости от браузера:
- Firefox: запрос между источниками заблокирован: та же политика источника запрещает чтение удаленного ресурса по адресу
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Причина: запрос CORS не выполнен). Подробнее - На основе Chromium: доступ к получению по адресу "https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5" из источникаhttps://cors3.azurewebsites.net "" был заблокирован политикой CORS: ответ на предварительный запрос не передает проверку контроля доступа: в запрошенном ресурсе отсутствует заголовок "Access-Control-Allow-Origin". Если этот непрозрачный ответ вам подходит, задайте для режима запроса значение "no-cors", чтобы извлечь ресурс с отключенным параметром CORS.
Чтобы разрешить определенные заголовки, вызовите WithHeaders:
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
// requires using Microsoft.Net.Http.Headers;
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
Чтобы разрешить все заголовки запросов автора, вызовите AllowAnyHeader:
options.AddPolicy("MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
Браузеры не согласованы в том, как они заданы Access-Control-Request-Headers
. Если одно из следующих вариантов:
- Заголовки имеют значение, отличное от других
"*"
- AllowAnyHeader вызывается: включите по крайней мере
Accept
,Content-Type
иOrigin
, а также все пользовательские заголовки, которые требуется поддерживать.
Код автоматического предварительного запроса
При применении политики CORS:
- Глобально путем вызова
app.UseCors
Startup.Configure
. - Использование атрибута
[EnableCors]
.
ASP.NET Core отвечает на запрос параметров предварительной проверки.
Включение CORS на основе каждой конечной точки с использованием RequireCors
в настоящее время не поддерживает автоматические предварительные запросы.
В разделе Test CORS этого документа показано это поведение.
Атрибут [HttpOptions] для предварительных запросов
Если CORS включена с соответствующей политикой, ASP.NET Core обычно реагирует на запросы предварительной проверки CORS автоматически. В некоторых сценариях это может быть не так. Например, использование CORS с маршрутизацией конечных точек.
Следующий код использует атрибут [HttpOptions] для создания конечных точек для запросов 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);
}
Инструкции по тестированию предыдущего кода см. в разделе Test CORS с маршрутизацией конечных точек и [HttpOptions] .
Установка времени окончания срока действия предварительного срока действия
Заголовок Access-Control-Max-Age
указывает, сколько времени можно кэшировать ответ на предварительный запрос. Чтобы задать этот заголовок, вызовите SetPreflightMaxAge:
options.AddPolicy("MySetPreflightExpirationPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
});
Как работает CORS
В этом разделе описывается, что происходит в запросе CORS на уровне HTTP-сообщений.
- CORS не является функцией безопасности. CORS — это стандарт W3C, позволяющий серверу расслабиться в политике одного источника.
- Например, злоумышленник может использовать межсайтовые скрипты (XSS) на вашем сайте и выполнить межсайтовый запрос на свой сайт с поддержкой CORS для кражи информации.
- API не безопаснее, разрешая CORS.
- Это до клиента (браузера) для принудительного применения CORS. Сервер выполняет запрос и возвращает ответ, он является клиентом, который возвращает ошибку и блокирует ответ. Например, любой из следующих средств отобразит ответ сервера:
- Fiddler
- HttpClient .NET
- Веб-браузер, введя URL-адрес в адресной строке.
- Это до клиента (браузера) для принудительного применения CORS. Сервер выполняет запрос и возвращает ответ, он является клиентом, который возвращает ошибку и блокирует ответ. Например, любой из следующих средств отобразит ответ сервера:
- Это способ для сервера, чтобы разрешить браузерам выполнять запрос XHR или API получения, который в противном случае будет запрещен.
- Браузеры без CORS не могут выполнять запросы между источниками. Перед CORS JSONP использовался для обхода этого ограничения. JSONP не использует XHR, он использует
<script>
тег для получения ответа. Скрипты могут загружаться между источниками.
- Браузеры без CORS не могут выполнять запросы между источниками. Перед CORS JSONP использовался для обхода этого ограничения. JSONP не использует XHR, он использует
Спецификация CORS представила несколько новых заголовков HTTP, которые позволяют выполнять запросы между источниками. Если браузер поддерживает CORS, он автоматически задает эти заголовки для запросов между источниками. Пользовательский код JavaScript не требуется для включения CORS.
Ниже приведен пример запроса между источниками из кнопки "Тест значений " в https://cors1.azurewebsites.net/api/values
. Заголовок Origin
:
- Предоставляет домен сайта, выполняющего запрос.
- Требуется и должен отличаться от узла.
Общие заголовки
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
Заголовки ответа
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
Заголовки запроса
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 ...
В OPTIONS
запросах сервер задает заголовок заголовков Access-Control-Allow-Origin: {allowed origin}
ответа в ответе. Например, развернутый пример запроса кнопки OPTIONS
Delete [EnableCors] содержит следующие заголовки:
Общие заголовки
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
Заголовки ответа
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
Заголовки запроса
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
В предыдущих заголовках ответа сервер задает заголовок Access-Control-Allow-Origin в ответе. Значение https://cors1.azurewebsites.net
этого заголовка соответствует заголовку Origin
из запроса.
Если AllowAnyOrigin вызывается, Access-Control-Allow-Origin: *
возвращается подстановочное значение. AllowAnyOrigin
разрешает любой источник.
Если ответ не включает Access-Control-Allow-Origin
заголовок, запрос между источниками завершается ошибкой. В частности, браузер запрещает запрос. Даже если сервер возвращает успешный ответ, браузер не делает ответ доступным для клиентского приложения.
Отображение запросов OPTIONS
По умолчанию браузеры Chrome и Edge не отображают запросы OPTIONS на сетевой вкладке инструментов F12. Чтобы отобразить запросы OPTIONS в этих браузерах, выполните следующие действия.
chrome://flags/#out-of-blink-cors
илиedge://flags/#out-of-blink-cors
- отключите флаг.
- перезапуск.
Firefox отображает запросы OPTIONS по умолчанию.
CORS в IIS
При развертывании в IIS CORS необходимо запустить перед проверкой подлинности Windows, если сервер не настроен для предоставления анонимного доступа. Для поддержки этого сценария необходимо установить и настроить модуль IIS CORS для приложения.
Тестирование CORS
Пример скачивания содержит код для тестирования CORS. См. раздел Практическое руководство. Скачивание файла. Примером является проект API с Razor добавленными страницами:
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();
});
}
}
Предупреждение
WithOrigins("https://localhost:<port>");
следует использовать только для тестирования примера приложения, аналогичного примеру кода скачивания.
ValuesController
Ниже приведены конечные точки для тестирования:
[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 предоставляется пакетом NuGet Rick.Docs.Samples.RouteInfo и отображает информацию о маршруте.
Протестируйте предыдущий пример кода с помощью одного из следующих подходов:
- Запустите пример с
dotnet run
использованием URL-адресаhttps://localhost:5001
по умолчанию. - Запустите пример из Visual Studio с портом 44398 для URL-адреса
https://localhost:44398
.
Использование браузера с инструментами F12:
Нажмите кнопку "Значения" и просмотрите заголовки на вкладке "Сеть".
Нажмите кнопку "Тест PUT ". Сведения о отображении запроса OPTIONS см. в разделе "Запросы ПАРАМЕТРОВ ". Тест PUT создает два запроса, предварительный запрос OPTIONS и запрос PUT.
Нажмите кнопку
GetValues2 [DisableCors]
, чтобы активировать неудачный запрос CORS. Как упоминалось в документе, ответ возвращает 200 успешно, но запрос CORS не выполняется. Перейдите на вкладку "Консоль", чтобы увидеть ошибку CORS. В зависимости от браузера отображается ошибка, аналогичная следующему:Доступ к получению
'https://cors1.azurewebsites.net/api/values/GetValues2'
из источника'https://cors3.azurewebsites.net'
заблокирован политикой CORS: в запрошенном ресурсе отсутствует заголовок Access-Control-Allow-Origin. Если этот непрозрачный ответ вам подходит, задайте для режима запроса значение "no-cors", чтобы извлечь ресурс с отключенным параметром CORS.
Конечные точки с поддержкой CORS можно протестировать с помощью средства, например curl или Fiddler. При использовании средства источник запроса, указанного Origin
заголовком, должен отличаться от узла, получающего запрос. Если запрос не является кросс-источником на основе значения заголовка Origin
:
- Для обработки запроса не требуется ПО промежуточного слоя CORS.
- Заголовки CORS не возвращаются в ответе.
Следующая команда используется curl
для выдачи запроса OPTIONS с информацией:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
Тестирование CORS с помощью маршрутизации конечных точек и [HttpOptions]
Включение CORS на основе каждой конечной точки с использованием RequireCors
в настоящее время не поддерживает автоматические предварительные запросы. Рассмотрим следующий код, использующий маршрутизацию конечных точек для включения 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();
});
}
}
TodoItems1Controller
Ниже приведены конечные точки для тестирования:
[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);
}
Проверьте предыдущий код на тестовой странице (https://cors1.azurewebsites.net/test?number=1
) развернутого примера.
Кнопки Delete [EnableCors] и GET [EnableCors] успешно выполнены, так как конечные точки имеют [EnableCors]
и отвечают на предварительные запросы. Сбой других конечных точек. Кнопка GET завершается ошибкой, так как JavaScript отправляет:
headers: {
"Content-Type": "x-custom-header"
},
TodoItems2Controller
Ниже приведены аналогичные конечные точки, но содержит явный код для реагирования на запросы 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);
}
Приведенный выше код можно проверить, развернув пример, чтобы Azure.In раскрывающемся списке контроллера , выберите предварительный просмотр и установите контроллер. Все вызовы CORS к TodoItems2Controller
конечным точкам успешно выполнены.
Дополнительные ресурсы
ASP.NET Core