ASP.NET Core でクロスオリジン要求 (CORS) を有効にする

メモ

これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の ASP.NET Core 8.0 バージョンを参照してください。

作成者: Rick Anderson および Kirk Larkin

この記事では、クロス オリジン リソース共有 (Cross-Origin Resource Sharing: CORS) を ASP.NET Core アプリ内で有効にする方法について説明します。

ブラウザーのセキュリティにより、Web ページを提供したドメインと異なるドメインに対して、Web ページが要求を行うことはできません。 この制限は、同一オリジン ポリシーと呼ばれます。 同一オリジン ポリシーにより、悪意のあるサイトが別のサイトから機密データを読み取れないようになっています。 場合によっては、自分のアプリに対して他のサイトがクロスオリジン要求を行うのを許可する必要があります。 詳しくは、Mozilla CORS に関する記事をご覧ください。

クロスオリジン リソース共有 (CORS):

  • サーバーが同一オリジン ポリシーを緩和できるようにする W3C 標準です。
  • セキュリティ機能ではありません。CORS はセキュリティを緩和します。 CORS を許可すると、API は安全ではなくなります。 詳しくは、「CORS のしくみ」をご覧ください。
  • サーバーが一部のクロスオリジン要求を明示的に許可し、それ以外を拒否できるようにします。
  • JSONP などの以前の手法よりは、安全性と柔軟性が向上しています。

サンプル コードを表示またはダウンロードします (ダウンロード方法)。

同じオリジン

2 つの URL は、スキーム、ホスト、ポートが同じ場合、同じオリジンを持ちます (RFC 6454)。

次の 2 つの URL は同じオリジンです。

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

これらの URL は、前の 2 つの URL と異なるオリジンです。

  • https://example.net: 異なるドメイン
  • https://www.example.com/foo.html: 異なるサブドメイン
  • http://example.com/foo.html: 異なるスキーム
  • https://example.com:9000/foo.html: 異なるポート

CORS を有効にする

CORS を有効にするには、3 つの方法があります。

[EnableCors] 属性と名前付きポリシーを使うと、CORS をサポートするエンドポイントの制限を最もきめ細かく制御できます。

警告

UseCors は正しい順序で呼び出す必要があります。 詳しくは、「ミドルウェアの順序」をご覧ください。 たとえば、UseResponseCaching を使うときは、UseResponseCaching の前に UseCors を呼び出す必要があります。

以下のセクションでは、各方法について詳しく説明します。

名前付きポリシーとミドルウェアを使用した 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 ポリシーの適用については、エンドポイント ルーティングに関する説明をご覧ください。
  • 応答キャッシュ ミドルウェアを使用する場合は、UseResponseCaching の前に UseCors を呼び出します。

エンドポイント ルーティングを使用するときは、UseRoutingUseEndpoints の呼び出しの間で実行するように、CORS ミドルウェアを構成する必要があります

前のコードと同様のコードをテストする方法については、「CORS のテスト」をご覧ください。

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 の順序

一般に、UseCors の前に UseStaticFiles が呼び出されます。 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 を有効にする

エンドポイント ルーティングでは、拡張メソッドの RequireCors セットを使用して、エンドポイントごとに 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");
                      });
});

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 Pages エンドポイントでは、既定のポリシーが指定されていないので、クロスオリジン要求は許可されません

[DisableCors] 属性を指定しても、エンドポイント ルーティングと RequireCors によって有効にされた CORS は無効になりません

前のものと同様のコードをテストする方法については、「[EnableCors] 属性と RequireCors メソッドを使用して CORS をテストする」をご覧ください。

属性を使用して CORS を有効にする

[EnableCors] 属性を使用して CORS を有効にし、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(),
        };
    }
}

次のコードでは、2 つの 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 要求の制限を最も細かく制御するには:

次のセクションのコードは、上のリストを満たしています。

前のコードと同様のコードをテストする方法については、「CORS のテスト」をご覧ください。

CORS を無効にする

[DisableCors] 属性を指定しても、エンドポイント ルーティングによって有効にされた CORS は無効になりません

次のコードでは、CORS ポリシー "MyPolicy" が定義されます。

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

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

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

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

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

app.Run();

次のコードでは、GetValues2 アクションの CORS が無効になります。

[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 ポリシーのオプション

ここでは、CORS ポリシーで設定できるさまざまなオプションについて説明します。

AddPolicyProgram.cs で呼び出されます。 一部のオプションについては、最初に「CORS のしくみ」セクションを読むと役に立つことがあります。

許可されるオリジンを設定する

AllowAnyOrigin: 任意のスキーム (http または https) を使用するすべてのオリジンからの CORS 要求を許可します。 AllowAnyOrigin は、"どの Web サイト" でもアプリに対してクロスオリジン要求を行えるので、安全ではありません。

Note

AllowAnyOriginAllowCredentials を指定する構成は安全ではなく、クロスサイト リクエスト フォージェリが発生する可能性があります。 アプリが両方の方法で構成されている場合、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 メソッドを設定する

AllowAnyMethod:

  • すべての 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 ヘッダーに影響します。 詳しくは、「プレフライト要求」セクションをご覧ください。

WithHeaders によって指定された特定のヘッダーに対する CORS ミドルウェア ポリシーの一致は、Access-Control-Request-Headers で送信されたヘッダーが WithHeaders で示されているヘッダーと正確に一致する場合にのみ可能です。

たとえば、次のように構成されたアプリについて考えます。

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

Content-Language (HeaderNames.ContentLanguage) が WithHeaders の一覧にないので、次の要求ヘッダーを持つプレフライト要求は CORS ミドルウェアによって拒否されます。

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.withCredentialstrue に設定する必要があります。

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

Fetch 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 ヘッダーが含まれていない場合は、ブラウザーからアプリに応答は公開されず、クロスオリジン要求は失敗します。

クロスオリジンの資格情報を許可すると、セキュリティ リスクになります。 別のドメインの Web サイトが、サインインしているユーザーの資格情報を、ユーザーが知らないうちに、ユーザーに代わってアプリに送信できます。

また、CORS の仕様では、Access-Control-Allow-Credentials ヘッダーが存在する場合、オリジンを "*" (すべてのオリジン) に設定するのは無効であると示されています。

プレフライト要求

一部の CORS 要求では、実際の要求が行われる前に、ブラウザーによって追加の OPTIONS 要求が送信されます。 この要求は、プレフライト要求と呼ばれます。 次の条件がすべて当てはまる場合、ブラウザーはプレフライト要求をスキップできます。

  • 要求メソッドが、GET、HEAD、または POST である。
  • アプリで、AcceptAccept-LanguageContent-LanguageContent-Type、または Last-Event-ID 以外の要求ヘッダーが設定されていない。
  • Content-Type ヘッダーが設定されている場合、次のいずれかの値が含まれる。
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

クライアント要求に設定される要求ヘッダーについての規則は、アプリで XMLHttpRequest オブジェクトの setRequestHeader を呼び出すことによって設定するヘッダーに適用されます。 CORS の仕様では、これらのヘッダーは作成者要求ヘッダーと呼ばれます。 User-AgentHostContent-Length など、ブラウザーで設定できるヘッダーにはこの規則は適用されません。

このドキュメントの「CORS のテスト」セクションの [Put test] ボタンから行われるプレフライト要求に似た応答の例を次に示します。

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 メソッドが使用されます。 次のヘッダーが含まれる場合があります。

プレフライト要求が拒否された場合、アプリからは 200 OK 応答が返されますが、CORS ヘッダーは設定されていません。 そのため、ブラウザーでクロスオリジン要求は試みられません。 拒否されたプレフライト要求の例については、このドキュメントの「CORS のテスト」セクションをご覧ください。

F12 ツールを使用すると、ブラウザーに応じて、次のいずれかのようなエラーがコンソール アプリに表示されます。

  • Firefox: クロスオリジン要求がブロックされました: 同一オリジン ポリシーでは、https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 でのリモート リソースの読み取りは許可されません。 (理由: CORS 要求が失敗しました)。 詳細情報
  • Chromium ベース: オリジン 'https://cors3.azurewebsites.net ' からの 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 ' でのフェッチへのアクセスが、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 が呼び出されます: 少なくとも AcceptContent-TypeOrigin と、サポートしたいカスタム ヘッダーを含めます。

自動プレフライト要求コード

CORS ポリシーが次のいずれかに適用されるとき:

  • Program.csapp.UseCors を呼び出すことによってグローバルに。
  • [EnableCors] 属性を使用。

ASP.NET Core は、プレフライト OPTIONS 要求に応答します。

このドキュメントの「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);
    }

前のコードをテストする方法については、「[EnableCors] 属性と RequireCors メソッドを使用して CORS をテストする」をご覧ください。

プレフライトの有効期限を設定する

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 はセキュリティ機能ではありません。 CORS は、サーバーで同一オリジン ポリシーを緩和できるようにする W3C 標準です。
  • CORS を許可すると、API は安全ではなくなります。
    • CORS を適用するかどうかは、クライアント (ブラウザー) に委ねられます。 サーバーは要求を実行して応答を返します。エラーを返して応答をブロックするのはクライアントです。 たとえば、次のツールを使用すると、サーバーの応答が表示されます。
  • これは、サーバーがブラウザーにクロスオリジンの XHR または Fetch API 要求の実行を許可する方法であり、それ以外の場合は禁止されます。
    • CORS を使用しないブラウザーは、クロスオリジン要求を行うことができません。 CORS より前は、ONPJS がこの制限を回避するために使用されました。 JSONP では XHR は使用されず、<script> タグを使用して応答を受信します。 スクリプトはクロスオリジンで読み込むことができます。

CORS の仕様では、クロスオリジン要求を可能にするいくつかの新しい HTTP ヘッダーが導入されました。 CORS をサポートしているブラウザーでは、クロスオリジン要求に対してこれらのヘッダーが自動的に設定されます。 CORS を有効にするためにカスタム JavaScript コードは必要ありません。

デプロイされたサンプル[PUT test] ボタン

次に示すのは、[Values] テスト ボタンから 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}のヘッダーが設定されます。 たとえば、デプロイされたサンプル[[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 ヘッダーが含まれない場合、クロスオリジン要求は失敗します。 具体的には、ブラウザーで要求が許可されません。 サーバーから正常応答が返された場合でも、クライアント アプリはブラウザーで応答を使用できません。

HTTPS への HTTP リダイレクトにより、CORS プレフライト要求で、ERR_INVALID_REDIRECT が発生する

UseHttpsRedirection による、HTTPS にリダイレクトされる HTTP を使用するエンドポイントへの要求は、ERR_INVALID_REDIRECT on the CORS preflight request で失敗します。

API プロジェクトでは、UseHttpsRedirection を使用して HTTPS に要求をリダイレクトするのではなく、HTTP 要求を拒否できます。

IIS での CORS

IIS に展開したとき、サーバーが匿名アクセスを許可するように構成されていない場合は、Windows 認証の前に CORS が動いている必要があります。 このシナリオをサポートするには、IIS CORS モジュールをインストールして、アプリ用に構成する必要があります。

CORS のテスト

サンプルのダウンロードには、CORS をテストするためのコードがあります。 ダウンロード方法に関するページを参照してください。 サンプルは、Razor Pages が追加された API プロジェクトです。

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

}

MyDisplayRouteInfoRick.Docs.Samples.RouteInfo NuGet パッケージによって提供され、ルート情報が表示されます。

次のいずれかの方法を使用して、上記のサンプル コードをテストします。

  • https://cors3.azurewebsites.net/ でデプロイされたサンプル アプリを使用します。 サンプルをダウンロードする必要はありません。
  • 既定の URL https://localhost:5001 を使用して、dotnet run でサンプルを実行します。
  • URL https://localhost:44398 用にポートを 44398 に設定して、Visual Studio からサンプルを実行します。

F12 ツールでのブラウザーの使用:

  • [Values] ボタンを選び、 [ネットワーク] タブでヘッダーを確認します。

  • [PUT test] ボタンを選びます。 OPTIONS 要求を表示する手順については、「OPTIONS 要求を表示する」をご覧ください。 [PUT test] を選ぶと、OPTIONS プレフライト要求と PUT 要求の 2 つの要求が作成されます。

  • 失敗する CORS 要求をトリガーするには、 [GetValues2 [DisableCors]] ボタンを選びます。 ドキュメントで説明されているように、応答で 200 成功は返りますが、CORS 要求は行われません。 [コンソール] タブを選ぶと、CORS エラーが表示されます。 ブラウザーに応じて、次のようなエラーが表示されます。

    オリジン 'https://cors3.azurewebsites.net' からの 'https://cors1.azurewebsites.net/api/values/GetValues2' でのフェッチへのアクセスが、CORS ポリシーによってブロックされました: 要求されたリソースに 'Access-Control-Allow-Origin' ヘッダーが存在しません。 非透過の応答が要求に対応している場合は、要求のモードを 'no-cors' に設定し、CORS を無効にしてリソースをフェッチしてください。

CORS 対応のエンドポイントは、curlFiddler などのツールを使ってテストできます。 ツールを使うときは、Origin ヘッダーで指定されている要求のオリジンが、要求を受信するホストと異なる必要があります。 要求が Origin ヘッダーの値に基づく "クロスオリジン" ではない場合:

  • CORS ミドルウェアで要求を処理する必要はありません。
  • 応答で CORS ヘッダーは返されません。

次のコマンドでは、curl を使用して、情報を含む OPTIONS 要求が発行されます。

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

[EnableCors] 属性と RequireCors メソッドを使用して 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.UseEndpoints(endpoints =>
{
    endpoints.MapGet("/echo",
        context => context.Response.WriteAsync("echo"))
        .RequireCors("MyPolicy");

    endpoints.MapControllers();
    endpoints.MapRazorPages();
});

app.Run();

/echo エンドポイントのみが RequireCors を使用して、指定したポリシーを使用してクロスオリジン要求を許可していることに注意してください。 以下のコントローラーでは、[EnableCors] 属性を使用して CORS を有効にします。

次の 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);
}

デプロイされたサンプルテスト ページから、前のコードをテストします。

エンドポイントに [EnableCors] があり、プレフライト要求に応答するので、 [Delete [EnableCors]] ボタンと [GET [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);
}

デプロイされたサンプルのテスト ページから、前のコードをテストします。 コントローラーのドロップダウン リストで [Preflight] を選んでから、 [Set Controller] を選びます。 TodoItems2Controller エンドポイントへの CORS の呼び出しはすべて成功します。

その他の技術情報

作成者: Rick Anderson および Kirk Larkin

この記事では、ASP.NET Core アプリで CORS を有効にする方法について説明します。

ブラウザーのセキュリティにより、Web ページを提供したドメインと異なるドメインに対して、Web ページが要求を行うことはできません。 この制限は、同一オリジン ポリシーと呼ばれます。 同一オリジン ポリシーにより、悪意のあるサイトが別のサイトから機密データを読み取れないようになっています。 場合によっては、自分のアプリに対して他のサイトがクロスオリジン要求を行うのを許可する必要があります。 詳しくは、Mozilla CORS に関する記事をご覧ください。

クロスオリジン リソース共有 (CORS):

  • サーバーが同一オリジン ポリシーを緩和できるようにする W3C 標準です。
  • セキュリティ機能ではありません。CORS はセキュリティを緩和します。 CORS を許可すると、API は安全ではなくなります。 詳しくは、「CORS のしくみ」をご覧ください。
  • サーバーが一部のクロスオリジン要求を明示的に許可し、それ以外を拒否できるようにします。
  • JSONP などの以前の手法よりは、安全性と柔軟性が向上しています。

サンプル コードを表示またはダウンロードします (ダウンロード方法)。

同じオリジン

2 つの URL は、スキーム、ホスト、ポートが同じ場合、同じオリジンを持ちます (RFC 6454)。

次の 2 つの URL は同じオリジンです。

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

これらの URL は、前の 2 つの URL と異なるオリジンです。

  • https://example.net: 異なるドメイン
  • https://www.example.com/foo.html: 異なるサブドメイン
  • http://example.com/foo.html: 異なるスキーム
  • https://example.com:9000/foo.html: 異なるポート

CORS を有効にする

CORS を有効にするには、3 つの方法があります。

[EnableCors] 属性と名前付きポリシーを使うと、CORS をサポートするエンドポイントの制限を最もきめ細かく制御できます。

警告

UseCors は正しい順序で呼び出す必要があります。 詳しくは、「ミドルウェアの順序」をご覧ください。 たとえば、UseResponseCaching を使うときは、UseResponseCaching の前に UseCors を呼び出す必要があります。

以下のセクションでは、各方法について詳しく説明します。

名前付きポリシーとミドルウェアを使用した 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 ポリシーの適用については、エンドポイント ルーティングに関する説明をご覧ください。
  • 応答キャッシュ ミドルウェアを使用する場合は、UseResponseCaching の前に UseCors を呼び出します。

エンドポイント ルーティングを使用するときは、UseRoutingUseEndpoints の呼び出しの間で実行するように、CORS ミドルウェアを構成する必要があります

前のコードと同様のコードをテストする方法については、「CORS のテスト」をご覧ください。

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 の順序

一般に、UseCors の前に UseStaticFiles が呼び出されます。 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 を有効にする

エンドポイント ルーティングでは、拡張メソッドの RequireCors セットを使用して、エンドポイントごとに 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");
                      });
});

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 Pages エンドポイントでは、既定のポリシーが指定されていないので、クロスオリジン要求は許可されません

[DisableCors] 属性を指定しても、エンドポイント ルーティングと RequireCors によって有効にされた CORS は無効になりません

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

前のものと同様のコードをテストする方法については、「[EnableCors] 属性と RequireCors メソッドを使用して CORS をテストする」をご覧ください。

属性を使用して CORS を有効にする

[EnableCors] 属性を使用して CORS を有効にし、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(),
        };
    }
}

次のコードでは、2 つの 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 要求の制限を最も細かく制御するには:

次のセクションのコードは、上のリストを満たしています。

前のコードと同様のコードをテストする方法については、「CORS のテスト」をご覧ください。

CORS を無効にする

[DisableCors] 属性を指定しても、エンドポイント ルーティングによって有効にされた CORS は無効になりません

次のコードでは、CORS ポリシー "MyPolicy" が定義されます。

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

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

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

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

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

app.Run();

次のコードでは、GetValues2 アクションの CORS が無効になります。

[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 ポリシーのオプション

ここでは、CORS ポリシーで設定できるさまざまなオプションについて説明します。

AddPolicyProgram.cs で呼び出されます。 一部のオプションについては、最初に「CORS のしくみ」セクションを読むと役に立つことがあります。

許可されるオリジンを設定する

AllowAnyOrigin: 任意のスキーム (http または https) を使用するすべてのオリジンからの CORS 要求を許可します。 AllowAnyOrigin は、"どの Web サイト" でもアプリに対してクロスオリジン要求を行えるので、安全ではありません。

Note

AllowAnyOriginAllowCredentials を指定する構成は安全ではなく、クロスサイト リクエスト フォージェリが発生する可能性があります。 アプリが両方の方法で構成されている場合、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 メソッドを設定する

AllowAnyMethod:

  • すべての 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 ヘッダーに影響します。 詳しくは、「プレフライト要求」セクションをご覧ください。

WithHeaders によって指定された特定のヘッダーに対する CORS ミドルウェア ポリシーの一致は、Access-Control-Request-Headers で送信されたヘッダーが WithHeaders で示されているヘッダーと正確に一致する場合にのみ可能です。

たとえば、次のように構成されたアプリについて考えます。

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

Content-Language (HeaderNames.ContentLanguage) が WithHeaders の一覧にないので、次の要求ヘッダーを持つプレフライト要求は CORS ミドルウェアによって拒否されます。

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.withCredentialstrue に設定する必要があります。

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

Fetch 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 ヘッダーが含まれていない場合は、ブラウザーからアプリに応答は公開されず、クロスオリジン要求は失敗します。

クロスオリジンの資格情報を許可すると、セキュリティ リスクになります。 別のドメインの Web サイトが、サインインしているユーザーの資格情報を、ユーザーが知らないうちに、ユーザーに代わってアプリに送信できます。

また、CORS の仕様では、Access-Control-Allow-Credentials ヘッダーが存在する場合、オリジンを "*" (すべてのオリジン) に設定するのは無効であると示されています。

プレフライト要求

一部の CORS 要求では、実際の要求が行われる前に、ブラウザーによって追加の OPTIONS 要求が送信されます。 この要求は、プレフライト要求と呼ばれます。 次の条件がすべて当てはまる場合、ブラウザーはプレフライト要求をスキップできます。

  • 要求メソッドが、GET、HEAD、または POST である。
  • アプリで、AcceptAccept-LanguageContent-LanguageContent-Type、または Last-Event-ID 以外の要求ヘッダーが設定されていない。
  • Content-Type ヘッダーが設定されている場合、次のいずれかの値が含まれる。
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

クライアント要求に設定される要求ヘッダーについての規則は、アプリで XMLHttpRequest オブジェクトの setRequestHeader を呼び出すことによって設定するヘッダーに適用されます。 CORS の仕様では、これらのヘッダーは作成者要求ヘッダーと呼ばれます。 User-AgentHostContent-Length など、ブラウザーで設定できるヘッダーにはこの規則は適用されません。

このドキュメントの「CORS のテスト」セクションの [Put test] ボタンから行われるプレフライト要求に似た応答の例を次に示します。

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 メソッドが使用されます。 次のヘッダーが含まれる場合があります。

プレフライト要求が拒否された場合、アプリからは 200 OK 応答が返されますが、CORS ヘッダーは設定されていません。 そのため、ブラウザーでクロスオリジン要求は試みられません。 拒否されたプレフライト要求の例については、このドキュメントの「CORS のテスト」セクションをご覧ください。

F12 ツールを使用すると、ブラウザーに応じて、次のいずれかのようなエラーがコンソール アプリに表示されます。

  • Firefox: クロスオリジン要求がブロックされました: 同一オリジン ポリシーでは、https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 でのリモート リソースの読み取りは許可されません。 (理由: CORS 要求が失敗しました)。 詳細情報
  • Chromium ベース: オリジン 'https://cors3.azurewebsites.net ' からの 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 ' でのフェッチへのアクセスが、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 が呼び出されます: 少なくとも AcceptContent-TypeOrigin と、サポートしたいカスタム ヘッダーを含めます。

自動プレフライト要求コード

CORS ポリシーが次のいずれかに適用されるとき:

  • Program.csapp.UseCors を呼び出すことによってグローバルに。
  • [EnableCors] 属性を使用。

ASP.NET Core は、プレフライト OPTIONS 要求に応答します。

このドキュメントの「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);
    }

前のコードをテストする方法については、「[EnableCors] 属性と RequireCors メソッドを使用して CORS をテストする」をご覧ください。

プレフライトの有効期限を設定する

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 はセキュリティ機能ではありません。 CORS は、サーバーで同一オリジン ポリシーを緩和できるようにする W3C 標準です。
  • CORS を許可すると、API は安全ではなくなります。
    • CORS を適用するかどうかは、クライアント (ブラウザー) に委ねられます。 サーバーは要求を実行して応答を返します。エラーを返して応答をブロックするのはクライアントです。 たとえば、次のツールを使用すると、サーバーの応答が表示されます。
  • これは、サーバーがブラウザーにクロスオリジンの XHR または Fetch API 要求の実行を許可する方法であり、それ以外の場合は禁止されます。
    • CORS を使用しないブラウザーは、クロスオリジン要求を行うことができません。 CORS より前は、ONPJS がこの制限を回避するために使用されました。 JSONP では XHR は使用されず、<script> タグを使用して応答を受信します。 スクリプトはクロスオリジンで読み込むことができます。

CORS の仕様では、クロスオリジン要求を可能にするいくつかの新しい HTTP ヘッダーが導入されました。 CORS をサポートしているブラウザーでは、クロスオリジン要求に対してこれらのヘッダーが自動的に設定されます。 CORS を有効にするためにカスタム JavaScript コードは必要ありません。

デプロイされたサンプル[PUT test] ボタン

次に示すのは、[Values] テスト ボタンから 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}のヘッダーが設定されます。 たとえば、デプロイされたサンプル[[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 ヘッダーが含まれない場合、クロスオリジン要求は失敗します。 具体的には、ブラウザーで要求が許可されません。 サーバーから正常応答が返された場合でも、クライアント アプリはブラウザーで応答を使用できません。

HTTPS への HTTP リダイレクトにより、CORS プレフライト要求で、ERR_INVALID_REDIRECT が発生する

UseHttpsRedirection による、HTTPS にリダイレクトされる HTTP を使用するエンドポイントへの要求は、ERR_INVALID_REDIRECT on the CORS preflight request で失敗します。

API プロジェクトでは、UseHttpsRedirection を使用して HTTPS に要求をリダイレクトするのではなく、HTTP 要求を拒否できます。

IIS での CORS

IIS に展開したとき、サーバーが匿名アクセスを許可するように構成されていない場合は、Windows 認証の前に CORS が動いている必要があります。 このシナリオをサポートするには、IIS CORS モジュールをインストールして、アプリ用に構成する必要があります。

CORS のテスト

サンプルのダウンロードには、CORS をテストするためのコードがあります。 ダウンロード方法に関するページを参照してください。 サンプルは、Razor Pages が追加された API プロジェクトです。

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

}

MyDisplayRouteInfoRick.Docs.Samples.RouteInfo NuGet パッケージによって提供され、ルート情報が表示されます。

次のいずれかの方法を使用して、上記のサンプル コードをテストします。

  • https://cors3.azurewebsites.net/ でデプロイされたサンプル アプリを使用します。 サンプルをダウンロードする必要はありません。
  • 既定の URL https://localhost:5001 を使用して、dotnet run でサンプルを実行します。
  • URL https://localhost:44398 用にポートを 44398 に設定して、Visual Studio からサンプルを実行します。

F12 ツールでのブラウザーの使用:

  • [Values] ボタンを選び、 [ネットワーク] タブでヘッダーを確認します。

  • [PUT test] ボタンを選びます。 OPTIONS 要求を表示する手順については、「OPTIONS 要求を表示する」をご覧ください。 [PUT test] を選ぶと、OPTIONS プレフライト要求と PUT 要求の 2 つの要求が作成されます。

  • 失敗する CORS 要求をトリガーするには、 [GetValues2 [DisableCors]] ボタンを選びます。 ドキュメントで説明されているように、応答で 200 成功は返りますが、CORS 要求は行われません。 [コンソール] タブを選ぶと、CORS エラーが表示されます。 ブラウザーに応じて、次のようなエラーが表示されます。

    オリジン 'https://cors3.azurewebsites.net' からの 'https://cors1.azurewebsites.net/api/values/GetValues2' でのフェッチへのアクセスが、CORS ポリシーによってブロックされました: 要求されたリソースに 'Access-Control-Allow-Origin' ヘッダーが存在しません。 非透過の応答が要求に対応している場合は、要求のモードを 'no-cors' に設定し、CORS を無効にしてリソースをフェッチしてください。

CORS 対応のエンドポイントは、curlFiddler などのツールを使ってテストできます。 ツールを使うときは、Origin ヘッダーで指定されている要求のオリジンが、要求を受信するホストと異なる必要があります。 要求が Origin ヘッダーの値に基づく "クロスオリジン" ではない場合:

  • CORS ミドルウェアで要求を処理する必要はありません。
  • 応答で CORS ヘッダーは返されません。

次のコマンドでは、curl を使用して、情報を含む OPTIONS 要求が発行されます。

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

[EnableCors] 属性と RequireCors メソッドを使用して 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.UseEndpoints(endpoints =>
{
    endpoints.MapGet("/echo",
        context => context.Response.WriteAsync("echo"))
        .RequireCors("MyPolicy");

    endpoints.MapControllers();
    endpoints.MapRazorPages();
});

app.Run();

/echo エンドポイントのみが RequireCors を使用して、指定したポリシーを使用してクロスオリジン要求を許可していることに注意してください。 以下のコントローラーでは、[EnableCors] 属性を使用して CORS を有効にします。

次の 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);
}

デプロイされたサンプルテスト ページから、前のコードをテストします。

エンドポイントに [EnableCors] があり、プレフライト要求に応答するので、 [Delete [EnableCors]] ボタンと [GET [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);
}

デプロイされたサンプルのテスト ページから、前のコードをテストします。 コントローラーのドロップダウン リストで [Preflight] を選んでから、 [Set Controller] を選びます。 TodoItems2Controller エンドポイントへの CORS の呼び出しはすべて成功します。

その他の技術情報

作成者: Rick Anderson および Kirk Larkin

この記事では、ASP.NET Core アプリで CORS を有効にする方法について説明します。

ブラウザーのセキュリティにより、Web ページを提供したドメインと異なるドメインに対して、Web ページが要求を行うことはできません。 この制限は、同一オリジン ポリシーと呼ばれます。 同一オリジン ポリシーにより、悪意のあるサイトが別のサイトから機密データを読み取れないようになっています。 場合によっては、自分のアプリに対して他のサイトがクロスオリジン要求を行うのを許可する必要があります。 詳しくは、Mozilla CORS に関する記事をご覧ください。

クロスオリジン リソース共有 (CORS):

  • サーバーが同一オリジン ポリシーを緩和できるようにする W3C 標準です。
  • セキュリティ機能ではありません。CORS はセキュリティを緩和します。 CORS を許可すると、API は安全ではなくなります。 詳しくは、「CORS のしくみ」をご覧ください。
  • サーバーが一部のクロスオリジン要求を明示的に許可し、それ以外を拒否できるようにします。
  • JSONP などの以前の手法よりは、安全性と柔軟性が向上しています。

サンプル コードを表示またはダウンロードします (ダウンロード方法)。

同じオリジン

2 つの URL は、スキーム、ホスト、ポートが同じ場合、同じオリジンを持ちます (RFC 6454)。

次の 2 つの URL は同じオリジンです。

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

これらの URL は、前の 2 つの URL と異なるオリジンです。

  • https://example.net: 異なるドメイン
  • https://www.example.com/foo.html: 異なるサブドメイン
  • http://example.com/foo.html: 異なるスキーム
  • https://example.com:9000/foo.html: 異なるポート

CORS を有効にする

CORS を有効にするには、3 つの方法があります。

[EnableCors] 属性と名前付きポリシーを使うと、CORS をサポートするエンドポイントの制限を最もきめ細かく制御できます。

警告

UseCors は正しい順序で呼び出す必要があります。 詳しくは、「ミドルウェアの順序」をご覧ください。 たとえば、UseResponseCaching を使うときは、UseResponseCaching の前に UseCors を呼び出す必要があります。

以下のセクションでは、各方法について詳しく説明します。

名前付きポリシーとミドルウェアを使用した 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 ポリシーの適用については、エンドポイント ルーティングに関する説明をご覧ください。
  • 応答キャッシュ ミドルウェアを使用する場合は、UseResponseCaching の前に UseCors を呼び出します。

エンドポイント ルーティングを使用するときは、UseRoutingUseEndpoints の呼び出しの間で実行するように、CORS ミドルウェアを構成する必要があります

前のコードと同様のコードをテストする方法については、「CORS のテスト」をご覧ください。

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 の順序

一般に、UseCors の前に UseStaticFiles が呼び出されます。 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 を有効にする

RequireCors を使用してエンドポイントごとに CORS を有効化する場合、"自動プレフライト要求はサポートされません。" 詳細については、この GitHub の問題と「エンドポイント ルーティングと [HttpOptions] を使用して CORS をテストする」を参照してください。

エンドポイント ルーティングでは、拡張メソッドの RequireCors セットを使用して、エンドポイントごとに 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");
                      });
});

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 Pages エンドポイントでは、既定のポリシーが指定されていないので、クロスオリジン要求は許可されません

[DisableCors] 属性を指定しても、エンドポイント ルーティングと RequireCors によって有効にされた CORS は無効になりません

前のものと同様のコードをテストする方法については、「エンドポイント ルーティングと [HttpOptions] を使用して CORS をテストする」をご覧ください。

属性を使用して CORS を有効にする

[EnableCors] 属性を使用して CORS を有効にし、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(),
        };
    }
}

次のコードでは、2 つの 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 要求の制限を最も細かく制御するには:

次のセクションのコードは、上のリストを満たしています。

前のコードと同様のコードをテストする方法については、「CORS のテスト」をご覧ください。

CORS を無効にする

[DisableCors] 属性を指定しても、エンドポイント ルーティングによって有効にされた CORS は無効になりません

次のコードでは、CORS ポリシー "MyPolicy" が定義されます。

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

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

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

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

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

app.Run();

次のコードでは、GetValues2 アクションの CORS が無効になります。

[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 ポリシーのオプション

ここでは、CORS ポリシーで設定できるさまざまなオプションについて説明します。

AddPolicyProgram.cs で呼び出されます。 一部のオプションについては、最初に「CORS のしくみ」セクションを読むと役に立つことがあります。

許可されるオリジンを設定する

AllowAnyOrigin: 任意のスキーム (http または https) を使用するすべてのオリジンからの CORS 要求を許可します。 AllowAnyOrigin は、"どの Web サイト" でもアプリに対してクロスオリジン要求を行えるので、安全ではありません。

Note

AllowAnyOriginAllowCredentials を指定する構成は安全ではなく、クロスサイト リクエスト フォージェリが発生する可能性があります。 アプリが両方の方法で構成されている場合、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 メソッドを設定する

AllowAnyMethod:

  • すべての 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 ヘッダーに影響します。 詳しくは、「プレフライト要求」セクションをご覧ください。

WithHeaders によって指定された特定のヘッダーに対する CORS ミドルウェア ポリシーの一致は、Access-Control-Request-Headers で送信されたヘッダーが WithHeaders で示されているヘッダーと正確に一致する場合にのみ可能です。

たとえば、次のように構成されたアプリについて考えます。

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

Content-Language (HeaderNames.ContentLanguage) が WithHeaders の一覧にないので、次の要求ヘッダーを持つプレフライト要求は CORS ミドルウェアによって拒否されます。

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.withCredentialstrue に設定する必要があります。

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

Fetch 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 ヘッダーが含まれていない場合は、ブラウザーからアプリに応答は公開されず、クロスオリジン要求は失敗します。

クロスオリジンの資格情報を許可すると、セキュリティ リスクになります。 別のドメインの Web サイトが、サインインしているユーザーの資格情報を、ユーザーが知らないうちに、ユーザーに代わってアプリに送信できます。

また、CORS の仕様では、Access-Control-Allow-Credentials ヘッダーが存在する場合、オリジンを "*" (すべてのオリジン) に設定するのは無効であると示されています。

プレフライト要求

一部の CORS 要求では、実際の要求が行われる前に、ブラウザーによって追加の OPTIONS 要求が送信されます。 この要求は、プレフライト要求と呼ばれます。 次の条件がすべて当てはまる場合、ブラウザーはプレフライト要求をスキップできます。

  • 要求メソッドが、GET、HEAD、または POST である。
  • アプリで、AcceptAccept-LanguageContent-LanguageContent-Type、または Last-Event-ID 以外の要求ヘッダーが設定されていない。
  • Content-Type ヘッダーが設定されている場合、次のいずれかの値が含まれる。
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

クライアント要求に設定される要求ヘッダーについての規則は、アプリで XMLHttpRequest オブジェクトの setRequestHeader を呼び出すことによって設定するヘッダーに適用されます。 CORS の仕様では、これらのヘッダーは作成者要求ヘッダーと呼ばれます。 User-AgentHostContent-Length など、ブラウザーで設定できるヘッダーにはこの規則は適用されません。

このドキュメントの「CORS のテスト」セクションの [Put test] ボタンから行われるプレフライト要求に似た応答の例を次に示します。

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 メソッドが使用されます。 次のヘッダーが含まれる場合があります。

プレフライト要求が拒否された場合、アプリからは 200 OK 応答が返されますが、CORS ヘッダーは設定されていません。 そのため、ブラウザーでクロスオリジン要求は試みられません。 拒否されたプレフライト要求の例については、このドキュメントの「CORS のテスト」セクションをご覧ください。

F12 ツールを使用すると、ブラウザーに応じて、次のいずれかのようなエラーがコンソール アプリに表示されます。

  • Firefox: クロスオリジン要求がブロックされました: 同一オリジン ポリシーでは、https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 でのリモート リソースの読み取りは許可されません。 (理由: CORS 要求が失敗しました)。 詳細情報
  • Chromium ベース: オリジン 'https://cors3.azurewebsites.net ' からの 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 ' でのフェッチへのアクセスが、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 が呼び出されます: 少なくとも AcceptContent-TypeOrigin と、サポートしたいカスタム ヘッダーを含めます。

自動プレフライト要求コード

CORS ポリシーが次のいずれかに適用されるとき:

  • Program.csapp.UseCors を呼び出すことによってグローバルに。
  • [EnableCors] 属性を使用。

ASP.NET Core は、プレフライト OPTIONS 要求に応答します。

現在、RequireCors を使ってエンドポイントごとに CORS を有効にすると、自動プレフライト要求はサポートされません

このドキュメントの「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);
    }

前のコードをテストする方法については、「エンドポイント ルーティングと [HttpOptions] を使用して CORS をテストする」をご覧ください。

プレフライトの有効期限を設定する

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 のしくみ

このセクションでは、HTTP メッセージのレベルでの CORS 要求の処理について説明します。

  • CORS はセキュリティ機能ではありません。 CORS は、サーバーで同一オリジン ポリシーを緩和できるようにする W3C 標準です。
  • CORS を許可すると、API は安全ではなくなります。
    • CORS を適用するかどうかは、クライアント (ブラウザー) に委ねられます。 サーバーは要求を実行して応答を返します。エラーを返して応答をブロックするのはクライアントです。 たとえば、次のツールを使用すると、サーバーの応答が表示されます。
  • これは、サーバーがブラウザーにクロスオリジンの XHR または Fetch API 要求の実行を許可する方法であり、それ以外の場合は禁止されます。
    • CORS を使用しないブラウザーは、クロスオリジン要求を行うことができません。 CORS より前は、ONPJS がこの制限を回避するために使用されました。 JSONP では XHR は使用されず、<script> タグを使用して応答を受信します。 スクリプトはクロスオリジンで読み込むことができます。

CORS の仕様では、クロスオリジン要求を可能にするいくつかの新しい HTTP ヘッダーが導入されました。 CORS をサポートしているブラウザーでは、クロスオリジン要求に対してこれらのヘッダーが自動的に設定されます。 CORS を有効にするためにカスタム JavaScript コードは必要ありません。

デプロイされたサンプル[PUT test] ボタン

次に示すのは、[Values] テスト ボタンから 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}のヘッダーが設定されます。 たとえば、デプロイされたサンプル[[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 ヘッダーが含まれない場合、クロスオリジン要求は失敗します。 具体的には、ブラウザーで要求が許可されません。 サーバーから正常応答が返された場合でも、クライアント アプリはブラウザーで応答を使用できません。

HTTPS への HTTP リダイレクトにより、CORS プレフライト要求で、ERR_INVALID_REDIRECT が発生する

UseHttpsRedirection による、HTTPS にリダイレクトされる HTTP を使用するエンドポイントへの要求は、ERR_INVALID_REDIRECT on the CORS preflight request で失敗します。

API プロジェクトでは、UseHttpsRedirection を使用して HTTPS に要求をリダイレクトするのではなく、HTTP 要求を拒否できます。

OPTIONS 要求を表示する

既定では、Chrome および Edge ブラウザーの F12 ツールのネットワーク タブに、OPTIONS 要求は表示されません。 これらのブラウザーで OPTIONS 要求を表示するには:

  • chrome://flags/#out-of-blink-cors または edge://flags/#out-of-blink-cors
  • フラグを無効にします。
  • [再起動] をタップします。

Firefox では、既定で OPTIONS 要求が表示されます。

IIS での CORS

IIS に展開したとき、サーバーが匿名アクセスを許可するように構成されていない場合は、Windows 認証の前に CORS が動いている必要があります。 このシナリオをサポートするには、IIS CORS モジュールをインストールして、アプリ用に構成する必要があります。

CORS のテスト

サンプルのダウンロードには、CORS をテストするためのコードがあります。 ダウンロード方法に関するページを参照してください。 サンプルは、Razor Pages が追加された API プロジェクトです。

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

}

MyDisplayRouteInfoRick.Docs.Samples.RouteInfo NuGet パッケージによって提供され、ルート情報が表示されます。

次のいずれかの方法を使用して、上記のサンプル コードをテストします。

  • https://cors3.azurewebsites.net/ でデプロイされたサンプル アプリを使用します。 サンプルをダウンロードする必要はありません。
  • 既定の URL https://localhost:5001 を使用して、dotnet run でサンプルを実行します。
  • URL https://localhost:44398 用にポートを 44398 に設定して、Visual Studio からサンプルを実行します。

F12 ツールでのブラウザーの使用:

  • [Values] ボタンを選び、 [ネットワーク] タブでヘッダーを確認します。

  • [PUT test] ボタンを選びます。 OPTIONS 要求を表示する手順については、「OPTIONS 要求を表示する」をご覧ください。 [PUT test] を選ぶと、OPTIONS プレフライト要求と PUT 要求の 2 つの要求が作成されます。

  • 失敗する CORS 要求をトリガーするには、 [GetValues2 [DisableCors]] ボタンを選びます。 ドキュメントで説明されているように、応答で 200 成功は返りますが、CORS 要求は行われません。 [コンソール] タブを選ぶと、CORS エラーが表示されます。 ブラウザーに応じて、次のようなエラーが表示されます。

    オリジン 'https://cors3.azurewebsites.net' からの 'https://cors1.azurewebsites.net/api/values/GetValues2' でのフェッチへのアクセスが、CORS ポリシーによってブロックされました: 要求されたリソースに 'Access-Control-Allow-Origin' ヘッダーが存在しません。 非透過の応答が要求に対応している場合は、要求のモードを 'no-cors' に設定し、CORS を無効にしてリソースをフェッチしてください。

CORS 対応のエンドポイントは、curlFiddler などのツールを使ってテストできます。 ツールを使うときは、Origin ヘッダーで指定されている要求のオリジンが、要求を受信するホストと異なる必要があります。 要求が Origin ヘッダーの値に基づく "クロスオリジン" ではない場合:

  • CORS ミドルウェアで要求を処理する必要はありません。
  • 応答で CORS ヘッダーは返されません。

次のコマンドでは、curl を使用して、情報を含む OPTIONS 要求が発行されます。

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

エンドポイント ルーティングと [HttpOptions] を使用して CORS をテストする

現在、RequireCors を使ってエンドポイントごとに CORS を有効にすると、自動プレフライト要求はサポートされません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);
}

デプロイされたサンプルテスト ページから、前のコードをテストします。

エンドポイントに [EnableCors] があり、プレフライト要求に応答するので、 [Delete [EnableCors]] ボタンと [GET [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);
}

デプロイされたサンプルのテスト ページから、前のコードをテストします。 コントローラーのドロップダウン リストで [Preflight] を選んでから、 [Set Controller] を選びます。 TodoItems2Controller エンドポイントへの CORS の呼び出しはすべて成功します。

その他の技術情報

作成者: Rick Anderson および Kirk Larkin

この記事では、ASP.NET Core アプリで CORS を有効にする方法について説明します。

ブラウザーのセキュリティにより、Web ページを提供したドメインと異なるドメインに対して、Web ページが要求を行うことはできません。 この制限は、同一オリジン ポリシーと呼ばれます。 同一オリジン ポリシーにより、悪意のあるサイトが別のサイトから機密データを読み取れないようになっています。 場合によっては、自分のアプリに対して他のサイトがクロスオリジン要求を行うのを許可する必要があります。 詳しくは、Mozilla CORS に関する記事をご覧ください。

クロスオリジン リソース共有 (CORS):

  • サーバーが同一オリジン ポリシーを緩和できるようにする W3C 標準です。
  • セキュリティ機能ではありません。CORS はセキュリティを緩和します。 CORS を許可すると、API は安全ではなくなります。 詳しくは、「CORS のしくみ」をご覧ください。
  • サーバーが一部のクロスオリジン要求を明示的に許可し、それ以外を拒否できるようにします。
  • JSONP などの以前の手法よりは、安全性と柔軟性が向上しています。

サンプル コードを表示またはダウンロードします (ダウンロード方法)。

同じオリジン

2 つの URL は、スキーム、ホスト、ポートが同じ場合、同じオリジンを持ちます (RFC 6454)。

次の 2 つの URL は同じオリジンです。

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

これらの URL は、前の 2 つの URL と異なるオリジンです。

  • https://example.net: 異なるドメイン
  • https://www.example.com/foo.html: 異なるサブドメイン
  • http://example.com/foo.html: 異なるスキーム
  • https://example.com:9000/foo.html: 異なるポート

CORS を有効にする

CORS を有効にするには、3 つの方法があります。

[EnableCors] 属性と名前付きポリシーを使うと、CORS をサポートするエンドポイントの制限を最もきめ細かく制御できます。

警告

UseCors は正しい順序で呼び出す必要があります。 詳しくは、「ミドルウェアの順序」をご覧ください。 たとえば、UseResponseCaching を使うときは、UseResponseCaching の前に UseCors を呼び出す必要があります。

以下のセクションでは、各方法について詳しく説明します。

名前付きポリシーとミドルウェアを使用した 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 ポリシーの適用については、エンドポイント ルーティングに関する説明をご覧ください。
  • 応答キャッシュ ミドルウェアを使用する場合は、UseResponseCaching の前に UseCors を呼び出します。

エンドポイント ルーティングを使用するときは、UseRoutingUseEndpoints の呼び出しの間で実行するように、CORS ミドルウェアを構成する必要があります

前のコードと同様のコードをテストする方法については、「CORS のテスト」をご覧ください。

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 を有効にする

RequireCors を使用してエンドポイントごとに CORS を有効化する場合、"自動プレフライト要求はサポートされません。" 詳細については、この GitHub の問題と「エンドポイント ルーティングと [HttpOptions] を使用して CORS をテストする」を参照してください。

エンドポイント ルーティングでは、拡張メソッドの RequireCors セットを使用して、エンドポイントごとに 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.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 Pages エンドポイントでは、既定のポリシーが指定されていないので、クロスオリジン要求は許可されません

[DisableCors] 属性を指定しても、エンドポイント ルーティングと RequireCors によって有効にされた CORS は無効になりません

前のものと同様のコードをテストする方法については、「エンドポイント ルーティングと [HttpOptions] を使用して CORS をテストする」をご覧ください。

属性を使用して CORS を有効にする

[EnableCors] 属性を使用して CORS を有効にし、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(),
        };
    }
}

次のコードでは、2 つの 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 要求の制限を最も細かく制御するには:

次のセクションのコードは、上のリストを満たしています。

前のコードと同様のコードをテストする方法については、「CORS のテスト」をご覧ください。

CORS を無効にする

[DisableCors] 属性を指定しても、エンドポイント ルーティングによって有効にされた CORS は無効になりません

次のコードでは、CORS ポリシー "MyPolicy" が定義されます。

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

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

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

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

        app.UseCors();

        app.UseAuthorization();

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

次のコードでは、GetValues2 アクションの CORS が無効になります。

[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 ポリシーのオプション

ここでは、CORS ポリシーで設定できるさまざまなオプションについて説明します。

AddPolicyStartup.ConfigureServices で呼び出されます。 一部のオプションについては、最初に「CORS のしくみ」セクションを読むと役に立つことがあります。

許可されるオリジンを設定する

AllowAnyOrigin: 任意のスキーム (http または https) を使用するすべてのオリジンからの CORS 要求を許可します。 AllowAnyOrigin は、"どの Web サイト" でもアプリに対してクロスオリジン要求を行えるので、安全ではありません。

Note

AllowAnyOriginAllowCredentials を指定する構成は安全ではなく、クロスサイト リクエスト フォージェリが発生する可能性があります。 アプリが両方の方法で構成されている場合、CORS サービスは無効な CORS 応答を返します。

AllowAnyOrigin は、プレフライト要求と Access-Control-Allow-Origin ヘッダーに影響します。 詳しくは、「プレフライト要求」セクションをご覧ください。

SetIsOriginAllowedToAllowWildcardSubdomains: ポリシーの IsOriginAllowed プロパティを、オリジンが許可されるかどうかを評価するときに、構成されたワイルドカード ドメインとオリジンが一致できるようにする関数に設定します。

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

許可される HTTP メソッドを設定する

AllowAnyMethod:

  • すべての 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 ヘッダーに影響します。 詳しくは、「プレフライト要求」セクションをご覧ください。

WithHeaders によって指定された特定のヘッダーに対する CORS ミドルウェア ポリシーの一致は、Access-Control-Request-Headers で送信されたヘッダーが WithHeaders で示されているヘッダーと正確に一致する場合にのみ可能です。

たとえば、次のように構成されたアプリについて考えます。

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

Content-Language (HeaderNames.ContentLanguage) が WithHeaders の一覧にないので、次の要求ヘッダーを持つプレフライト要求は CORS ミドルウェアによって拒否されます。

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.withCredentialstrue に設定する必要があります。

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

Fetch 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 ヘッダーが含まれていない場合は、ブラウザーからアプリに応答は公開されず、クロスオリジン要求は失敗します。

クロスオリジンの資格情報を許可すると、セキュリティ リスクになります。 別のドメインの Web サイトが、サインインしているユーザーの資格情報を、ユーザーが知らないうちに、ユーザーに代わってアプリに送信できます。

また、CORS の仕様では、Access-Control-Allow-Credentials ヘッダーが存在する場合、オリジンを "*" (すべてのオリジン) に設定するのは無効であると示されています。

プレフライト要求

一部の CORS 要求では、実際の要求が行われる前に、ブラウザーによって追加の OPTIONS 要求が送信されます。 この要求は、プレフライト要求と呼ばれます。 次の条件がすべて当てはまる場合、ブラウザーはプレフライト要求をスキップできます。

  • 要求メソッドが、GET、HEAD、または POST である。
  • アプリで、AcceptAccept-LanguageContent-LanguageContent-Type、または Last-Event-ID 以外の要求ヘッダーが設定されていない。
  • Content-Type ヘッダーが設定されている場合、次のいずれかの値が含まれる。
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

クライアント要求に設定される要求ヘッダーについての規則は、アプリで XMLHttpRequest オブジェクトの setRequestHeader を呼び出すことによって設定するヘッダーに適用されます。 CORS の仕様では、これらのヘッダーは作成者要求ヘッダーと呼ばれます。 User-AgentHostContent-Length など、ブラウザーで設定できるヘッダーにはこの規則は適用されません。

このドキュメントの「CORS のテスト」セクションの [Put test] ボタンから行われるプレフライト要求に似た応答の例を次に示します。

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 メソッドが使用されます。 次のヘッダーが含まれる場合があります。

プレフライト要求が拒否された場合、アプリからは 200 OK 応答が返されますが、CORS ヘッダーは設定されていません。 そのため、ブラウザーでクロスオリジン要求は試みられません。 拒否されたプレフライト要求の例については、このドキュメントの「CORS のテスト」セクションをご覧ください。

F12 ツールを使用すると、ブラウザーに応じて、次のいずれかのようなエラーがコンソール アプリに表示されます。

  • Firefox: クロスオリジン要求がブロックされました: 同一オリジン ポリシーでは、https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 でのリモート リソースの読み取りは許可されません。 (理由: CORS 要求が失敗しました)。 詳細情報
  • Chromium ベース: オリジン 'https://cors3.azurewebsites.net ' からの 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 ' でのフェッチへのアクセスが、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 が呼び出されます: 少なくとも AcceptContent-TypeOrigin と、サポートしたいカスタム ヘッダーを含めます。

自動プレフライト要求コード

CORS ポリシーが次のいずれかに適用されるとき:

  • Startup.Configureapp.UseCors を呼び出すことによってグローバルに。
  • [EnableCors] 属性を使用。

ASP.NET Core は、プレフライト OPTIONS 要求に応答します。

現在、RequireCors を使ってエンドポイントごとに CORS を有効にすると、自動プレフライト要求はサポートされません

このドキュメントの「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);
    }

前のコードをテストする方法については、「エンドポイント ルーティングと [HttpOptions] を使用して CORS をテストする」をご覧ください。

プレフライトの有効期限を設定する

Access-Control-Max-Age ヘッダーでは、プレフライト要求への応答をキャッシュできる期間が指定されています。 このヘッダーを設定するには、SetPreflightMaxAge を呼び出します。

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

CORS のしくみ

このセクションでは、HTTP メッセージのレベルでの CORS 要求の処理について説明します。

  • CORS はセキュリティ機能ではありません。 CORS は、サーバーで同一オリジン ポリシーを緩和できるようにする W3C 標準です。
  • CORS を許可すると、API は安全ではなくなります。
    • CORS を適用するかどうかは、クライアント (ブラウザー) に委ねられます。 サーバーは要求を実行して応答を返します。エラーを返して応答をブロックするのはクライアントです。 たとえば、次のツールを使用すると、サーバーの応答が表示されます。
  • これは、サーバーがブラウザーにクロスオリジンの XHR または Fetch API 要求の実行を許可する方法であり、それ以外の場合は禁止されます。
    • CORS を使用しないブラウザーは、クロスオリジン要求を行うことができません。 CORS より前は、ONPJS がこの制限を回避するために使用されました。 JSONP では XHR は使用されず、<script> タグを使用して応答を受信します。 スクリプトはクロスオリジンで読み込むことができます。

CORS の仕様では、クロスオリジン要求を可能にするいくつかの新しい HTTP ヘッダーが導入されました。 CORS をサポートしているブラウザーでは、クロスオリジン要求に対してこれらのヘッダーが自動的に設定されます。 CORS を有効にするためにカスタム JavaScript コードは必要ありません。

デプロイされたサンプル[PUT test] ボタン

次に示すのは、[Values] テスト ボタンから 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}のヘッダーが設定されます。 たとえば、デプロイされたサンプル[[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 ヘッダーが含まれない場合、クロスオリジン要求は失敗します。 具体的には、ブラウザーで要求が許可されません。 サーバーから正常応答が返された場合でも、クライアント アプリはブラウザーで応答を使用できません。

OPTIONS 要求を表示する

既定では、Chrome および Edge ブラウザーの F12 ツールのネットワーク タブに、OPTIONS 要求は表示されません。 これらのブラウザーで OPTIONS 要求を表示するには:

  • chrome://flags/#out-of-blink-cors または edge://flags/#out-of-blink-cors
  • フラグを無効にします。
  • [再起動] をタップします。

Firefox では、既定で OPTIONS 要求が表示されます。

IIS での CORS

IIS に展開したとき、サーバーが匿名アクセスを許可するように構成されていない場合は、Windows 認証の前に CORS が動いている必要があります。 このシナリオをサポートするには、IIS CORS モジュールをインストールして、アプリ用に構成する必要があります。

CORS のテスト

サンプルのダウンロードには、CORS をテストするためのコードがあります。 ダウンロード方法に関するページを参照してください。 サンプルは、Razor Pages が追加された API プロジェクトです。

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

}

MyDisplayRouteInfoRick.Docs.Samples.RouteInfo NuGet パッケージによって提供され、ルート情報が表示されます。

次のいずれかの方法を使用して、上記のサンプル コードをテストします。

  • https://cors3.azurewebsites.net/ でデプロイされたサンプル アプリを使用します。 サンプルをダウンロードする必要はありません。
  • 既定の URL https://localhost:5001 を使用して、dotnet run でサンプルを実行します。
  • URL https://localhost:44398 用にポートを 44398 に設定して、Visual Studio からサンプルを実行します。

F12 ツールでのブラウザーの使用:

  • [Values] ボタンを選び、 [ネットワーク] タブでヘッダーを確認します。

  • [PUT test] ボタンを選びます。 OPTIONS 要求を表示する手順については、「OPTIONS 要求を表示する」をご覧ください。 [PUT test] を選ぶと、OPTIONS プレフライト要求と PUT 要求の 2 つの要求が作成されます。

  • 失敗する CORS 要求をトリガーするには、 [GetValues2 [DisableCors]] ボタンを選びます。 ドキュメントで説明されているように、応答で 200 成功は返りますが、CORS 要求は行われません。 [コンソール] タブを選ぶと、CORS エラーが表示されます。 ブラウザーに応じて、次のようなエラーが表示されます。

    オリジン 'https://cors3.azurewebsites.net' からの 'https://cors1.azurewebsites.net/api/values/GetValues2' でのフェッチへのアクセスが、CORS ポリシーによってブロックされました: 要求されたリソースに 'Access-Control-Allow-Origin' ヘッダーが存在しません。 非透過の応答が要求に対応している場合は、要求のモードを 'no-cors' に設定し、CORS を無効にしてリソースをフェッチしてください。

CORS 対応のエンドポイントは、curlFiddler などのツールを使ってテストできます。 ツールを使うときは、Origin ヘッダーで指定されている要求のオリジンが、要求を受信するホストと異なる必要があります。 要求が Origin ヘッダーの値に基づく "クロスオリジン" ではない場合:

  • CORS ミドルウェアで要求を処理する必要はありません。
  • 応答で CORS ヘッダーは返されません。

次のコマンドでは、curl を使用して、情報を含む OPTIONS 要求が発行されます。

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

エンドポイント ルーティングと [HttpOptions] を使用して CORS をテストする

現在、RequireCors を使ってエンドポイントごとに CORS を有効にすると、自動プレフライト要求はサポートされません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);
}

デプロイされたサンプルテスト ページから、前のコードをテストします。

エンドポイントに [EnableCors] があり、プレフライト要求に応答するので、 [Delete [EnableCors]] ボタンと [GET [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);
}

デプロイされたサンプルのテスト ページから、前のコードをテストします。 コントローラーのドロップダウン リストで [Preflight] を選んでから、 [Set Controller] を選びます。 TodoItems2Controller エンドポイントへの CORS の呼び出しはすべて成功します。

その他の技術情報