ASP.NET Core에서 CORS (원본 간 요청) 사용

작성자: Rick AndersonKirk Larkin

이 문서에서는 ASP.NET Core 앱에서 CORS를 사용 하도록 설정 하는 방법을 보여 줍니다.

브라우저 보안 때문에 웹 페이지에서 해당 웹 페이지를 제공한 도메인이 아닌 다른 도메인에 요청을 수행할 수는 없습니다. 이러한 제한 사항을 동일 원본 정책 이라고 합니다. 동일 원본 정책은 악성 사이트에서 다른 사이트의 중요한 데이터를 읽지 못하도록 차단합니다. 경우에 따라 다른 사이트에서 앱에 대 한 원본 간 요청을 수행할 수 있도록 허용 하는 것이 좋습니다. 자세한 내용은 MOZILLA CORS 문서를 참조 하세요.

CORS ( 원본 간 리소스 공유 ):

  • 는 서버에서 동일한 원본 정책을 완화할 수 있게 해 주는 W3C 표준입니다.
  • 는 CORS 완화 security의 보안 기능이 아닙니다 . API는 CORS를 허용 하 여 안전 하지 않습니다. 자세한 내용은 CORS 작동 방식을 참조 하세요.
  • 다른 서버를 거부 하는 동안 서버에서 일부 교차 원본 요청을 명시적으로 허용할 수 있습니다.
  • JSONP와 같은 이전 기술 보다 안전 하 고 유연 합니다.

예제 코드 살펴보기 및 다운로드 (다운로드 방법)

동일한 원점

동일한 스키마, 호스트 및 포트 (RFC 6454)가 있는 경우 두 url의 원본이 동일 합니다.

이러한 두 Url의 출처는 동일 합니다.

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

이러한 Url의 원본은 이전 두 Url과 다릅니다.

  • https://example.net: 다른 도메인
  • https://www.example.com/foo.html: 다른 하위 도메인
  • http://example.com/foo.html: 다른 구성표
  • https://example.com:9000/foo.html: 다른 포트

CORS를 사용하도록 설정

CORS를 사용 하도록 설정 하는 방법에는 세 가지가 있습니다.

명명 된 정책에서 [EnableCors] 특성을 사용 하면 CORS를 지 원하는 끝점을 제한 하는 가장 세밀 한 제어를 제공 합니다.

경고

UseCors 올바른 순서로 호출 해야 합니다. 자세한 내용은 미들웨어 순서를 참조 하세요. 예를 들어를 UseCors 사용 하는 경우를 먼저 호출 해야 UseResponseCaching UseResponseCaching 합니다.

각 접근 방식은 다음 섹션에 자세히 설명 되어 있습니다.

명명 된 정책 및 미들웨어를 사용 하는 CORS

CORS 미들웨어는 크로스-원본 요청을 처리 합니다. 다음 코드는 지정 된 원본을 사용 하 여 모든 앱의 끝점에 CORS 정책을 적용 합니다.

public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyAllowSpecificOrigins,
                              builder =>
                              {
                                  builder.WithOrigins("http://example.com",
                                                      "http://www.contoso.com");
                              });
        });

        // services.AddResponseCaching();
        services.AddControllers();
    }

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

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

        app.UseCors(MyAllowSpecificOrigins);

        // app.UseResponseCaching();

        app.UseAuthorization();

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

위의 코드는

  • 정책 이름을로 설정 _myAllowSpecificOrigins 합니다. 정책 이름은 임의로입니다.
  • UseCors확장 메서드를 호출 하 고 _myAllowSpecificOrigins CORS 정책을 지정 합니다. UseCors CORS 미들웨어를 추가 합니다. 에 대 UseCors 한 호출은 앞에 배치 해야 UseRouting UseAuthorization 합니다. 자세한 내용은 미들웨어 순서를 참조 하세요.
  • AddCors 람다 식을사용 하 여를 호출 합니다. 람다는 개체를 사용 CorsPolicyBuilder 합니다. 등의 구성 옵션WithOrigins 대해서는이 문서의 뒷부분에서 설명 합니다.
  • _myAllowSpecificOrigins모든 컨트롤러 끝점에 대해 CORS 정책을 사용 하도록 설정 합니다. 특정 끝점에 CORS 정책을 적용 하려면 끝점 라우팅 을 참조 하세요.
  • 응답 캐싱 미들웨어를 사용 하는 경우 앞에서를 호출 UseCors UseResponseCaching 합니다.

끝점 라우팅을 사용 하 여 및에 대 한 호출 사이에 CORS 미들웨어를 실행 하도록 구성 해야 합니다 UseRouting UseEndpoints .

이전 코드와 유사한 코드 테스트에 대 한 지침은 CORS 테스트 를 참조 하세요.

AddCors메서드 호출은 CORS 서비스를 앱의 서비스 컨테이너에 추가 합니다.

public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyAllowSpecificOrigins,
                              builder =>
                              {
                                  builder.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,
                          builder =>
                          {
                              builder.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(
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                                        "http://www.contoso.com");
                });
        });

        services.AddControllers();
    }

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

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

        app.UseCors();

        app.UseAuthorization();

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

위의 코드는 모든 컨트롤러 끝점에 기본 CORS 정책을 적용 합니다.

엔드포인트 라우팅을 사용하여 Cors 사용

를 사용 하 여 끝점 단위로 CORS를 사용 하도록 설정 하면 RequireCors 자동 실행 전 요청을 지원 하지 않습니다. 자세한 내용은 끝점 라우팅을 사용 하 여 CORS를 발급 하 고 테스트 하는 GitHub 및 [httpoptions]를 참조 하세요.

끝점 라우팅을 사용 하면 확장 메서드 집합을 사용 하 여 끝점 별로 CORS를 사용 하도록 설정할 수 있습니다 RequireCors .

public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyAllowSpecificOrigins,
                              builder =>
                              {
                                  builder.WithOrigins("http://example.com",
                                                      "http://www.contoso.com");
                              });
        });

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

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

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

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapGet("/echo",
                context => context.Response.WriteAsync("echo"))
                .RequireCors(MyAllowSpecificOrigins);

            endpoints.MapControllers()
                     .RequireCors(MyAllowSpecificOrigins);

            endpoints.MapGet("/echo2",
                context => context.Response.WriteAsync("echo2"));

            endpoints.MapRazorPages();
        });
    }
}

위의 코드에서

  • app.UseCors CORS 미들웨어를 사용 하도록 설정 합니다. 기본 정책은 구성 되지 않았기 때문에 app.UseCors() CORS를 사용 하도록 설정 하지 않습니다.
  • /echo및 컨트롤러 끝점은 지정 된 정책을 사용 하 여 원본 간 요청을 허용 합니다.
  • /echo2 Razor 기본 정책을 지정 하지 않았으므로 및 페이지 끝점에서 원본 간 요청을 허용 하지 않습니다 .

[DisableCors] 특성은를 사용 하 여 끝점 라우팅을 통해 설정 된 CORS를 사용 하지 않도록 설정 RequireCors 합니다.

위와 비슷한 코드 테스트에 대 한 지침은 끝점 라우팅을 사용 하 여 CORS 테스트 및 [HttpOptions] 를 참조 하세요.

특성과 함께 CORS 사용

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

다음 코드는 두 개의 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",
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                                        "http://www.contoso.com");
                });

            options.AddPolicy("AnotherPolicy",
                builder =>
                {
                    builder.WithOrigins("http://www.contoso.com")
                                        .AllowAnyHeader()
                                        .AllowAnyMethod();
                });
        });

        services.AddControllers();
    }

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

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

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

CORS 요청 제한의 가장 적합 한 제어:

  • 명명 된 정책에를 사용 [EnableCors("MyPolicy")] 합니다.
  • 기본 정책을 정의 하지 않습니다.
  • 끝점 라우팅을사용 하지 않습니다.

다음 섹션의 코드는 위의 목록을 충족 합니다.

이전 코드와 유사한 코드 테스트에 대 한 지침은 CORS 테스트 를 참조 하세요.

CORS 사용 안 함

[DisableCors] 특성은 끝점 라우팅으로설정 된 CORS를 사용 하지 않도록 설정 하지 않습니다.

다음 코드에서는 CORS 정책을 정의 합니다 "MyPolicy" .

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

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

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

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

        app.UseCors();

        app.UseAuthorization();

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

다음 코드는 작업에 대해 CORS를 사용 하지 않도록 설정 합니다 GetValues2 .

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

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

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


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

}

위의 코드는

  • 끝점 라우팅을사용 하 여 CORS를 사용 하도록 설정 하지 않습니다.
  • 기본 CORS 정책을 정의하지 않습니다.
  • [EnableCors("MyPolicy")]를 사용하여 "MyPolicy" 컨트롤러에 대해 CORS 정책을 사용하도록 설정합니다.
  • 메서드에 대해 CORS를 사용하지 않도록 GetValues2 설정합니다.

이전 코드 테스트에 대한 지침은 CORS 테스트를 참조하세요.

CORS 정책 옵션

이 섹션에서는 CORS 정책에서 설정할 수 있는 다양한 옵션에 대해 설명합니다.

AddPolicy 는 에서 Startup.ConfigureServices 호출됩니다. 일부 옵션의 경우 CORS 작동 방식 섹션을 먼저 읽어보세요.

허용되는 원본 설정

AllowAnyOrigin: 모든 체계( 또는 )를 통해 모든 원본의 CORS 요청을 http https 허용합니다. AllowAnyOrigin모든 웹 사이트에서 앱에 대한 원본 간 요청을 만들 수 있으므로 안전하지 않습니다.

참고

AllowAnyOriginAllowCredentials를 지정하는 것은 안전하지 않은 구성이며 사이트 간 요청이 위조될 수 있습니다. 앱이 두 가지 방법으로 구성된 경우 CORS 서비스는 잘못된 CORS 응답을 반환합니다.

AllowAnyOrigin 는 사용 전 요청 및 헤더에 영향을 Access-Control-Allow-Origin 미칩니다. 자세한 내용은 Preflight requests 섹션을 참조하세요.

SetIsOriginAllowedToAllowWildcardSubdomains: IsOriginAllowed 정책의 속성을 원본이 허용되는지 평가할 때 구성된 와일드카드 도메인과 일치하도록 허용하는 함수로 설정합니다.

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

허용되는 HTTP 메서드 설정

AllowAnyMethod:

허용되는 요청 헤더 설정

작성자 요청헤더라고 하는 CORS 요청에서 특정 헤더를 보낼 수 있도록 하려면 를 WithHeaders 호출하고 허용된 헤더를 지정합니다.

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

모든 작성자 요청 헤더를허용하려면 를 호출합니다. AllowAnyHeader

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

AllowAnyHeader 는 사용 전 요청 및 Access-Control-Request-Headers 헤더에 영향을 미칩니다. 자세한 내용은 Preflight requests 섹션을 참조하세요.

에 지정된 특정 헤더와 일치하는 CORS 미들웨어 정책은 WithHeaders 에서 보낸 헤더가 에 명시된 헤더와 정확히 일치하는 경우에만 Access-Control-Request-Headers WithHeaders 가능합니다.

예를 들어 다음과 같이 구성된 앱을 고려합니다.

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

CORS 미들웨어는 Content-Language (HeaderNames.ContentLanguage)가에 나열되지 않으므로 다음 요청 헤더를 사용하여 준비 요청을 거부합니다. WithHeaders

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

앱은 200 OK 응답을 반환하지만 CORS 헤더를 다시 보내지 않습니다. 따라서 브라우저는 원본 간 요청을 시도하지 않습니다.

노출된 응답 헤더 설정

기본적으로 브라우저는 앱에 모든 응답 헤더를 노출하지 않습니다. 자세한 내용은 W3C 원본 간 리소스 공유(용어): 단순 응답 헤더 를 참조하세요.

기본적으로 사용할 수 있는 응답 헤더는 다음과 같습니다.

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

CORS 사양은 이러한 헤더 단순 응답 헤더 를 호출합니다. 앱에서 다른 헤더를 사용할 수 있도록 하려면 를 WithExposedHeaders 호출합니다.

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

원본 간 요청의 자격 증명

자격 증명은 CORS 요청에서 특별한 처리가 필요합니다. 기본적으로 브라우저는 원본 간 요청과 함께 자격 증명을 보내지 않습니다. 자격 증명에는 cookie 및 HTTP 인증 체계가 포함됩니다. 원본 간 요청으로 자격 증명을 보내려면 클라이언트가 를 로 설정해야 XMLHttpRequest.withCredentials true 합니다.

XMLHttpRequest직접 사용:

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

jQuery 사용:

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

Fetch API사용:

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

서버에서 자격 증명을 허용해야 합니다. 원본 간 자격 증명을 허용하려면 를 AllowCredentials 호출합니다.

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

HTTP 응답에는 Access-Control-Allow-Credentials 서버가 원본 간 요청에 대한 자격 증명을 허용한다는 것을 브라우저에 알리는 헤더가 포함됩니다.

브라우저에서 자격 증명을 보내지만 응답에 유효한 헤더가 포함되지 않은 경우 Access-Control-Allow-Credentials 브라우저는 앱에 응답을 노출하지 않으며 원본 간 요청이 실패합니다.

원본 간 자격 증명을 허용하는 것은 보안상 위험합니다. 다른 도메인의 웹 사이트는 사용자의 지식 없이 사용자를 대신하여 로그인한 사용자의 자격 증명을 앱에 보낼 수 있습니다.

CORS 사양은 헤더가 있는 경우 원본을 "*" (모든 원본)로 설정하는 것도 유효하지 않다고 Access-Control-Allow-Credentials 명시합니다.

준비 요청

일부 CORS 요청의 경우 브라우저는 실제 요청을 만들기 전에 추가 OPTIONS 요청을 보냅니다. 이 요청을 사전 요청이라고하며, 다음 조건이 모두 충족되면 브라우저에서 준비 요청을 건너뛸 수 있습니다.

  • 요청 메서드는 GET, HEAD 또는 POST입니다.
  • 앱은 , 또는 이외의 요청 헤더를 설정하지 Accept Accept-Language Content-Language Content-Type Last-Event-ID 않습니다.
  • Content-Type헤더가 설정된 경우 다음 값 중 하나가 있습니다.
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

클라이언트 요청에 대해 설정된 요청 헤더에 대한 규칙은 개체에서 를 호출하여 앱이 설정하는 setRequestHeader 헤더에 XMLHttpRequest 적용됩니다. CORS 사양은 이러한 헤더 작성자 요청 헤더 를 호출합니다. 규칙은 브라우저에서 설정할 수 있는 헤더(예: User-Agent , 또는 )에 적용되지 Host Content-Length 않습니다.

다음은 이 문서의 CORS 테스트 섹션에 있는 [테스트 배치] 단추에서 수행된 준비 요청과 유사한 응답 예제입니다.

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

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

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

준비 요청은 HTTP OPTIONS 메서드를 사용합니다. 여기에는 다음 헤더가 포함될 수 있습니다.

사용 전 요청이 거부되면 앱은 200 OK 응답을 반환하지만 CORS 헤더를 설정하지는 않습니다. 따라서 브라우저는 원본 간 요청을 시도하지 않습니다. 거부된 준비 요청의 예제는 이 문서의 CORS 테스트 섹션을 참조하세요.

F12 도구를 사용하면 콘솔 앱은 브라우저에 따라 다음 중 하나와 유사한 오류를 표시합니다.

  • Firefox: 원본 간 요청 차단: 동일한 원본 정책에서 에서 원격 리소스를 읽을 수 https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 없습니다. (이유: CORS 요청이 성공하지 못함). 자세한 내용
  • Chromium 기반: ' ' from origin ' '에서 페치할 수 있는 https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 https://cors3.azurewebsites.net 액세스가 CORS 정책에 의해 차단되었습니다. 수행 전 요청에 대한 응답이 액세스 제어 검사를 통과하지 않습니다. 요청된 리소스에 'Access-Control-Allow-Origin' 헤더가 없습니다. 사용자 요구에 불명확한 응답이 제공되면 요청 모드를 'no-cors'로 설정하여 CORS가 사용되지 않도록 설정된 리소스를 가져옵니다.

특정 헤더를 허용하려면 를 WithHeaders 호출합니다.

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

모든 작성자 요청 헤더를허용하려면 를 호출합니다. AllowAnyHeader

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

브라우저는 를 설정하는 방식과 일치하지 Access-Control-Request-Headers 않습니다. 다음 중 하나일 경우:

  • 헤더는 이외의 값으로 설정됩니다. "*"
  • AllowAnyHeader 가 호출됩니다. Accept 지원하려는 사용자 지정 헤더와 함께 , 및 이상을 Content-Type Origin 포함합니다.

자동 수행 전 요청 코드

CORS 정책이 적용되는 경우:

  • 에서 를 호출하여 전역적으로 app.UseCors Startup.Configure
  • 특성을 사용 하 여 [EnableCors]

ASP.NET Core 사용 전 OPTIONS 요청에 응답합니다.

현재 를 사용하여 엔드포인트별로 CORS를 사용하도록 RequireCors 설정해도 자동 작동 전 요청은 지원되지 않습니다.

이 문서의 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);
    }

위의 코드를 테스트 하는 방법에 대 한 지침은 끝점 라우팅을 사용 하 여 CORS 테스트 및 [HttpOptions] 를 참조 하세요.

실행 전 만료 시간 설정

헤더는 실행 Access-Control-Max-Age 전 요청에 대 한 응답을 캐시할 수 있는 기간을 지정 합니다. 이 헤더를 설정 하려면 다음을 호출 합니다 SetPreflightMaxAge .

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

CORS 작동 방법

이 섹션에서는 HTTP 메시지 수준에서 CORS 요청에 발생 하는 작업에 대해 설명 합니다.

  • CORS는 보안 기능이 아닙니다 . CORS는 서버에서 동일한 원본 정책을 완화할 수 있게 해 주는 W3C 표준입니다.
    • 예를 들어 악의적인 행위자는 사이트에 대해 XSS (교차 사이트 스크립팅) 를 사용 하 고 CORS 지원 사이트에 대 한 교차 사이트 요청을 실행 하 여 정보를 도용할 수 있습니다.
  • API는 CORS를 허용 하 여 안전 하지 않습니다.
    • CORS를 적용 하는 것은 클라이언트 (브라우저)에 있습니다. 서버는 요청을 실행 하 고 응답을 반환 합니다. 클라이언트는 오류를 반환 하 고 응답을 차단 합니다. 예를 들어 다음 도구 중 하나는 서버 응답을 표시 합니다.
  • 서버에서 브라우저가 원본 간 Xhr 또는 Fetch API 요청을 실행 하도록 허용 하는 방법입니다. 그렇지 않으면이를 사용할 수 없습니다.
    • CORS가 없는 브라우저는 크로스-원본 요청을 수행할 수 없습니다. CORS 이전에는 JSONP 을 사용 하 여이 제한을 회피 했습니다. JSONP는 XHR을 사용 하지 않으며 태그를 사용 하 여 <script> 응답을 수신 합니다. 스크립트를 크로스-원본으로 로드할 수 있습니다.

CORS 사양 에는 크로스-원본 요청을 가능 하 게 하는 몇 가지 새로운 HTTP 헤더가 도입 되었습니다. 브라우저에서 CORS를 지 원하는 경우 원본 간 요청에 대해 이러한 헤더를 자동으로 설정 합니다. CORS를 사용 하도록 설정 하는 데 사용자 지정 JavaScript 코드가 필요 하지 않습니다.

배포 된 샘플 에 대 한 테스트 배치 단추

다음은 테스트 단추에서로의 원본 간 요청 예제입니다 https://cors1.azurewebsites.net/api/values . Origin헤더:

  • 요청을 수행 하는 사이트의 도메인을 제공 합니다.
  • 는 필수 이며 호스트와 달라 야 합니다.

일반 헤더

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

응답 헤더

Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET

요청 헤더

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

OPTIONS요청에서 서버는 응답에 응답 헤더 헤더를 설정 합니다 Access-Control-Allow-Origin: {allowed origin} . 예를 들어, 배포 된 샘플Delete [EnableCors] 단추 OPTIONS 요청은 다음 헤더를 포함 합니다.

일반 헤더

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

응답 헤더

Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET

요청 헤더

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

위의 응답 헤더 에서 서버는 응답에 액세스 제어 허용 원본 헤더를 설정 합니다. https://cors1.azurewebsites.net이 헤더의 값은 요청의 헤더와 일치 합니다 Origin .

AllowAnyOrigin가 호출 되 면 Access-Control-Allow-Origin: * 와일드 카드 값이 반환 됩니다. AllowAnyOrigin 모든 원본을 허용 합니다.

응답에 헤더가 포함 되지 않은 경우 Access-Control-Allow-Origin 원본 간 요청이 실패 합니다. 특히 브라우저에서 요청을 허용 하지 않습니다. 서버에서 성공적인 응답을 반환 하는 경우에도 브라우저에서 응답을 클라이언트 앱에 사용할 수 있도록 설정 하지 않습니다.

표시 옵션 요청

기본적으로 Chrome 및 Edge 브라우저는 F12 도구의 네트워크 탭에서 옵션 요청을 표시 하지 않습니다. 이러한 브라우저에 옵션 요청을 표시 하려면 다음을 수행 합니다.

  • chrome://flags/#out-of-blink-cors 또는 edge://flags/#out-of-blink-cors
  • 플래그를 사용 하지 않습니다.
  • 시작할지.

Firefox에는 기본적으로 옵션 요청이 표시 됩니다.

IIS의 CORS

IIS에 배포할 때 서버에서 익명 액세스를 허용 하도록 구성 되지 않은 경우에는 인증을 Windows 하기 전에 CORS를 실행 해야 합니다. 이 시나리오를 지원 하려면 앱에 대해 IIS CORS 모듈 을 설치 하 고 구성 해야 합니다.

CORS 테스트

샘플 다운로드 에는 CORS를 테스트 하는 코드가 있습니다. 다운로드하는 방법을 참조하세요. 이 샘플은 페이지가 추가 된 API 프로젝트입니다 Razor .

public class StartupTest2
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: "MyPolicy",
                builder =>
                {
                    builder.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/ 합니다. 샘플을 다운로드할 필요가 없습니다.
  • dotnet run의 기본 URL을 사용 하 여 샘플을 실행 합니다 https://localhost:5001 .
  • URL에 대해 44398로 설정 된 포트를 사용 하 여 Visual Studio에서 샘플을 실행 합니다 https://localhost:44398 .

F12 도구를 사용 하 여 브라우저 사용:

  • 단추를 선택 하 고 네트워크 탭에서 헤더를 검토 합니다.

  • 테스트 배치 단추를 선택 합니다. 옵션 요청 표시에 대 한 지침은 옵션 요청 표시 를 참조 하세요. Put 테스트 는 옵션 실행 전 요청 및 PUT 요청 이라는 두 개의 요청을 만듭니다.

  • GetValues2 [DisableCors] 실패 한 CORS 요청을 트리거하는 단추를 선택 합니다. 문서에 설명 된 것 처럼 응답은 200 성공을 반환 하지만 CORS 요청은 수행 되지 않습니다. CORS 오류를 보려면 콘솔 탭을 선택 합니다. 브라우저에 따라 다음과 같은 오류가 표시 됩니다.

    원본에서 가져오기에 대 한 액세스가 'https://cors1.azurewebsites.net/api/values/GetValues2' 'https://cors3.azurewebsites.net' CORS 정책에 의해 차단 되었습니다. 요청 된 리소스에 ' 액세스 제어-원본 ' 헤더가 없습니다. 사용자 요구에 불명확한 응답이 제공되면 요청 모드를 'no-cors'로 설정하여 CORS가 사용되지 않도록 설정된 리소스를 가져옵니다.

CORS 사용 끝점은 말아 Fiddler또는 postman과 같은 도구를 사용 하 여 테스트할 수 있습니다. 도구를 사용 하는 경우 헤더에 의해 지정 된 요청의 원점은 Origin 요청을 받는 호스트와 달라 야 합니다. 요청이 헤더의 값을 기반으로 하는 교차 원본이 아닌 경우 Origin :

  • CORS 미들웨어가 요청을 처리할 필요가 없습니다.
  • CORS 헤더는 응답에서 반환 되지 않습니다.

다음 명령은를 사용 curl 하 여 정보를 포함 하는 옵션 요청을 실행 합니다.

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

끝점 라우팅을 사용 하 여 CORS 테스트 및 [HttpOptions]

현재를 사용 하 여 끝점 별로 CORS를 사용 하도록 설정 하면 RequireCors 자동 실행 전 요청을 지원 하지 않습니다. 끝점 라우팅을 사용 하 여 CORS를 사용 하도록 설정 하는 다음 코드를 고려 합니다.

public class StartupEndPointBugTest
{
    readonly string MyPolicy = "_myPolicy";

    // .WithHeaders(HeaderNames.ContentType, "x-custom-header")
    // forces browsers to require a preflight request with GET

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyPolicy,
                builder =>
                {
                    builder.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);
}

배포 된 샘플테스트 페이지 에서 이전 코드를 테스트 합니다.

끝점이 실행 전 요청에 응답 하 고이 요청에 응답 하기 때문에 Delete [EnableCors]GET [EnableCors] 단추가 성공 합니다 [EnableCors] . 다른 끝점은 실패 합니다. JavaScript 가 다음을 보내기 때문에 GET 단추가 실패 합니다.

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

배포 된 샘플의 테스트 페이지 에서 이전 코드를 테스트 합니다. 컨트롤러 드롭다운 목록에서 실행 을 선택 하 고 컨트롤러를 설정 합니다. 끝점에 대 한 모든 CORS 호출이 TodoItems2Controller 성공 했습니다.

추가 리소스

작성자: Rick AndersonKirk Larkin

이 문서에서는 ASP.NET Core 앱에서 CORS를 사용하도록 설정하는 방법을 보여 줍니다.

브라우저 보안 때문에 웹 페이지에서 해당 웹 페이지를 제공한 도메인이 아닌 다른 도메인에 요청을 수행할 수는 없습니다. 이러한 제한 사항을 동일 원본 정책 이라고 합니다. 동일 원본 정책은 악성 사이트에서 다른 사이트의 중요한 데이터를 읽지 못하도록 차단합니다. 경우에 따라 다른 사이트에서 앱에 대한 원본 간 요청을 만들도록 허용할 수 있습니다. 자세한 내용은 Mozilla CORS 문서 를 참조하세요.

CORS(원본 간 리소스 공유):

  • 서버에서 동일한 원본 정책을 완화할 수 있도록 하는 W3C 표준입니다.
  • 보안 기능이 아닌 CORS는 보안을 완화합니다. API는 CORS를 허용하여 안전하지 않습니다. 자세한 내용은 CORS 작동 방식을 참조하세요.
  • 서버에서 일부 원본 간 요청을 명시적으로 허용하면서 다른 요청을 거부할 수 있습니다.
  • JSONP와 같은 이전 기술보다 안전하고 유연합니다.

예제 코드 살펴보기 및 다운로드 (다운로드 방법)

동일한 원본

두 URL은 동일한 체계, 호스트 및포트(RFC 6454)가 있는 경우 동일한 원본을 갖습니다.

이러한 두 URL의 원본은 동일합니다.

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

이러한 URL의 원본은 이전 두 URL과 다릅니다.

  • https://example.net: 다른 도메인
  • https://www.example.com/foo.html: 다른 하위 도메인
  • http://example.com/foo.html: 다른 체계
  • https://example.com:9000/foo.html: 다른 포트

CORS를 사용하도록 설정

CORS를 사용하도록 설정하는 방법에는 세 가지가 있습니다.

명명된 정책과 함께 [EnableCors] 특성을 사용하면 CORS를 지원하는 엔드포인트를 제한할 수 있습니다.

경고

UseCors 는 올바른 순서로 호출되어야 합니다. 자세한 내용은 미들웨어 순서를 참조하세요. 예를 들어 를 UseCors 사용할 때는 이전에 를 호출해야 UseResponseCaching UseResponseCaching 합니다.

각 방법은 다음 섹션에 자세히 설명되어 있습니다.

명명된 정책 및 미들웨어가 있는 CORS

CORS 미들웨어는 원본 간 요청을 처리합니다. 다음 코드는 지정된 원본이 있는 모든 앱의 엔드포인트에 CORS 정책을 적용합니다.

public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyAllowSpecificOrigins,
                              builder =>
                              {
                                  builder.WithOrigins("http://example.com",
                                                      "http://www.contoso.com");
                              });
        });

        // services.AddResponseCaching();
        services.AddControllers();
    }

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

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

        app.UseCors(MyAllowSpecificOrigins);

        // app.UseResponseCaching();

        app.UseAuthorization();

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

위의 코드는

  • 정책 이름을 로 _myAllowSpecificOrigins 설정합니다. 정책 이름은 임의로 지정됩니다.
  • 확장 UseCors 메서드를 호출하고 _myAllowSpecificOrigins CORS 정책을 지정합니다. UseCors 는 CORS 미들웨어를 추가합니다. 에 대한 호출은 UseCors 이후, 는 앞에 배치해야 UseRouting UseAuthorization 합니다. 자세한 내용은 미들웨어 순서를 참조하세요.
  • AddCors 람다 식을통해 를 호출합니다. 람다는 CorsPolicyBuilder 개체를 가져옵니다. 같은 구성 옵션은 WithOrigins 이 문서의 후반부에서 설명합니다.
  • _myAllowSpecificOrigins모든 컨트롤러 엔드포인트에 대해 CORS 정책을 사용하도록 설정합니다. 특정 엔드포인트에 CORS 정책을 적용하려면 엔드포인트 라우팅을 참조하세요.
  • 응답 캐싱 미들웨어를사용하는 경우 전에 를 UseCors UseResponseCaching 호출합니다.

엔드포인트 라우팅을 통해 CORS 미들웨어는 및 호출 간에 실행되도록 구성되어야 UseRouting UseEndpoints 합니다.

이전 코드와 유사한 코드 테스트에 대한 지침은 CORS 테스트를 참조하세요.

AddCors메서드 호출은 앱의 서비스 컨테이너에 CORS 서비스를 추가합니다.

public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyAllowSpecificOrigins,
                              builder =>
                              {
                                  builder.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,
                          builder =>
                          {
                              builder.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(
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                                        "http://www.contoso.com");
                });
        });

        services.AddControllers();
    }

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

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

        app.UseCors();

        app.UseAuthorization();

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

위의 코드는 모든 컨트롤러 엔드포인트에 기본 CORS 정책을 적용합니다.

엔드포인트 라우팅을 사용하여 Cors 사용

를 사용하여 엔드포인트별로 CORS를 사용하도록 RequireCors 설정해도 자동 작동 전 요청은지원되지 않습니다. 자세한 내용은 이 GitHub 문제엔드포인트 라우팅을 통해 CORS 테스트 및 [HttpOptions] 를 참조하세요.

엔드포인트 라우팅을 사용하면 확장 메서드 집합을 사용하여 엔드포인트별로 CORS를 사용하도록 설정할 수 있습니다. RequireCors

public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyAllowSpecificOrigins,
                              builder =>
                              {
                                  builder.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] 특성은 를 사용하여 엔드포인트 라우팅에 의해 활성화된 CORS를 사용하지 않도록 설정하지 RequireCors 않습니다.

이전과 유사한 코드 테스트에 대한 지침은 엔드포인트 라우팅을 사용하여 CORS 테스트 및 [HttpOptions]를 참조하세요.

특성으로 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(),
        };
    }
}

다음 코드는 두 개의 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",
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                                        "http://www.contoso.com");
                });

            options.AddPolicy("AnotherPolicy",
                builder =>
                {
                    builder.WithOrigins("http://www.contoso.com")
                                        .AllowAnyHeader()
                                        .AllowAnyMethod();
                });
        });

        services.AddControllers();
    }

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

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

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

CORS 요청을 가장 잘 제어하려면 다음을 수행합니다.

  • [EnableCors("MyPolicy")]명명된 정책과 함께 사용합니다.
  • 기본 정책을 정의하지 마세요.
  • 엔드포인트 라우팅 을사용하지 마세요.

다음 섹션의 코드는 이전 목록을 충족합니다.

이전 코드와 유사한 코드 테스트에 대한 지침은 CORS 테스트를 참조하세요.

CORS 사용 안 함

[DisableCors] 특성은 엔드포인트 라우팅에서 사용하도록 설정된 CORS를 사용하지 않도록 설정하지 않습니다.

다음 코드는 CORS 정책 를 정의합니다. "MyPolicy"

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

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

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

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

        app.UseCors();

        app.UseAuthorization();

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

다음 코드는 작업에 대해 CORS를 사용하지 않도록 설정합니다. GetValues2

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

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

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


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

}

위의 코드는

위의 코드를 테스트 하는 방법에 대 한 지침은 CORS 테스트 를 참조 하세요.

CORS 정책 옵션

이 섹션에서는 CORS 정책에서 설정할 수 있는 다양 한 옵션에 대해 설명 합니다.

AddPolicy 는에서 호출 됩니다 Startup.ConfigureServices . 일부 옵션의 경우 CORS 작동 방법 섹션을 먼저 읽는 것이 도움이 될 수 있습니다.

허용 되는 원본 설정

AllowAnyOrigin: 모든 원본에서 임의의 스키마 (또는)로 CORS 요청을 허용 http https 합니다. AllowAnyOrigin모든 웹 사이트 에서 앱에 대 한 원본 간 요청을 수행할 수 있기 때문에은 안전 하지 않습니다.

참고

AllowAnyOriginAllowCredentials를 지정하는 것은 안전하지 않은 구성이며 사이트 간 요청이 위조될 수 있습니다. 앱이 두 가지 방법으로 구성된 경우 CORS 서비스는 잘못된 CORS 응답을 반환합니다.

AllowAnyOrigin 실행 전 요청 및 헤더에 영향을 줍니다 Access-Control-Allow-Origin . 자세한 내용은 실행 전 요청 섹션을 참조 하세요.

SetIsOriginAllowedToAllowWildcardSubdomains: IsOriginAllowed 원본이 허용 되는지 평가할 때 구성 된 와일드 카드 도메인에 대 한 원본을 허용 하는 함수로 정책의 속성을 설정 합니다.

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

허용 되는 HTTP 메서드 설정

AllowAnyMethod:

  • 모든 HTTP 메서드를 허용 합니다.
  • 실행 전 요청 및 헤더에 영향을 줍니다 Access-Control-Allow-Methods . 자세한 내용은 실행 전 요청 섹션을 참조 하세요.

허용 되는 요청 헤더 설정

작성자 요청 헤더라는 CORS 요청에서 특정 헤더를 보낼 수 있게 하려면를 호출 하 WithHeaders 고 허용 되는 헤더를 지정 합니다.

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

모든 작성자 요청 헤더를 허용 하려면 다음을 호출 합니다 AllowAnyHeader .

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

AllowAnyHeader 실행 전 요청 및 액세스 제어 요청 헤더 헤더에 영향을 줍니다. 자세한 내용은 실행 전 요청 섹션을 참조 하세요.

로 지정 된 특정 헤더와 일치 하는 CORS 미들웨어 정책은에 지정 된 헤더와 정확 하 게 일치 하는 WithHeaders 경우에만 가능 합니다 Access-Control-Request-Headers WithHeaders .

예를 들어 다음과 같이 구성 된 앱을 살펴보겠습니다.

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

CORS 미들웨어는 다음 요청 헤더를 사용 하 여 실행 전 요청을 거부 Content-Language 합니다 (Headernames. contentlanguage) WithHeaders .

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

앱은 200 OK 응답을 반환 하지만 CORS 헤더는 다시 전송 하지 않습니다. 따라서 브라우저에서 원본 간 요청을 시도 하지 않습니다.

노출 된 응답 헤더 설정

기본적으로 브라우저는 모든 응답 헤더를 앱에 노출 하지 않습니다. 자세한 내용은 W3C 원본 간 리소스 공유 (용어): 단순 응답 헤더를 참조 하세요.

기본적으로 사용할 수 있는 응답 헤더는 다음과 같습니다.

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

CORS 사양은 이러한 헤더 단순 응답 헤더 를 호출 합니다. 앱에서 다른 헤더를 사용할 수 있도록 하려면 다음을 호출 합니다 WithExposedHeaders .

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

원본 간 요청에 대 한 자격 증명

자격 증명을 CORS 요청에서 특수 하 게 처리 해야 합니다. 기본적으로 브라우저는 원본 간 요청과 함께 자격 증명을 보내지 않습니다. 자격 증명 cookie 에는 및 HTTP 인증 스키마가 포함 됩니다. 원본 간 요청을 사용 하 여 자격 증명을 보내려면 클라이언트는를로 설정 해야 합니다 XMLHttpRequest.withCredentials true .

XMLHttpRequest직접 사용:

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

JQuery 사용:

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

FETCH API사용:

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

서버에서 자격 증명을 허용 해야 합니다. 원본 간 자격 증명을 허용 하려면 다음을 호출 합니다 AllowCredentials .

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

HTTP 응답에는 Access-Control-Allow-Credentials 서버에서 원본 간 요청에 대 한 자격 증명을 허용 하도록 브라우저에 지시 하는 헤더가 포함 되어 있습니다.

브라우저에서 자격 증명을 전송 하지만 응답에 유효한 헤더가 포함 되지 않은 경우 Access-Control-Allow-Credentials 브라우저에서 앱에 응답을 노출 하지 않으며 크로스-원본 요청이 실패 합니다.

크로스-원본 자격 증명을 허용 하는 것은 보안상 위험 합니다. 다른 도메인의 웹 사이트는 사용자의 지식 없이 사용자를 대신 하 여 사용자의 앱에 로그인 한 사용자의 자격 증명을 보낼 수 있습니다.

CORS 사양은 "*" 헤더가 있는 경우 원본으로 설정 (모든 원본)을 사용할 수 없다는 것을 Access-Control-Allow-Credentials 나타냅니다.

실행 전 요청

일부 CORS 요청의 경우 브라우저는 실제 요청을 만들기 전에 추가 옵션 요청을 보냅니다. 이 요청을 실행 전 요청이라고 합니다. 다음 조건이 모두 충족 되는 경우 브라우저에서 실행 전 요청을 건너뛸 수 있습니다.

  • 요청 메서드는 GET, HEAD 또는 POST입니다.
  • 앱은 Accept ,, Accept-Language Content-Language , Content-Type 또는 이외의 요청 헤더를 설정 하지 않습니다 Last-Event-ID .
  • Content-Type헤더 (설정 된 경우)에는 다음 값 중 하나가 있습니다.
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

클라이언트 요청에 대해 설정 된 요청 헤더에 대 한 규칙은 응용 프로그램에서 개체에 대해를 호출 하 여 설정 하는 헤더에 적용 됩니다 setRequestHeader XMLHttpRequest . CORS 사양은 이러한 헤더 작성자 요청 헤더를 호출 합니다. 이 규칙은 브라우저에서 설정할 수 있는 헤더 (예:, 또는)에는 적용 되지 않습니다 User-Agent Host Content-Length .

다음은이 문서의 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 기반: ' ' 원본에서 ' '의 fetch에 대 한 액세스가 https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 https://cors3.azurewebsites.net CORS 정책에 의해 차단 되었습니다. 실행 전 요청에 대 한 응답은 액세스 제어 확인을 통과 하지 않습니다. 요청한 리소스에 ' 액세스 제어-허용-원본 ' 헤더가 없습니다. 사용자 요구에 불명확한 응답이 제공되면 요청 모드를 'no-cors'로 설정하여 CORS가 사용되지 않도록 설정된 리소스를 가져옵니다.

특정 헤더를 허용 하려면 다음을 호출 합니다 WithHeaders .

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

모든 작성자 요청 헤더를 허용 하려면 다음을 호출 합니다 AllowAnyHeader .

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

브라우저의 설정 방법은 일관 되지 않습니다 Access-Control-Request-Headers . 다음 중 하나입니다.

  • 헤더는 다음 이외의 값으로 설정 됩니다. "*"
  • AllowAnyHeader 가 호출 됩니다 .에는 Accept , Content-Type 및를 포함 하 고 Origin 지원 하려는 사용자 지정 헤더를 포함 합니다.

자동 실행 전 요청 코드

CORS 정책이 적용 되는 경우 다음 중 하나를 수행 합니다.

  • 에서를 호출 하 여 전역적으로 app.UseCors Startup.Configure
  • 특성을 사용 [EnableCors] 합니다.

ASP.NET Core는 실행 전 옵션 요청에 응답 합니다.

현재를 사용 하 여 끝점 별로 CORS를 사용 하도록 설정 하면 RequireCors 자동 실행 전 요청을 지원 하지 않습니다.

이 문서의 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);
    }

위의 코드를 테스트 하는 방법에 대 한 지침은 끝점 라우팅을 사용 하 여 CORS 테스트 및 [HttpOptions] 를 참조 하세요.

실행 전 만료 시간 설정

Access-Control-Max-Age헤더는 시작 전 요청에 대한 응답을 캐시할 수 있는 길이를 지정합니다. 이 헤더를 설정하려면 를 SetPreflightMaxAge 호출합니다.

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

CORS 작동 방식

이 섹션에서는 HTTP 메시지 수준에서 CORS 요청에서 발생하는 작업을 설명합니다.

  • CORS는 보안 기능이 아닙니다. CORS는 서버가 동일한 원본 정책을 완화할 수 있도록 하는 W3C 표준입니다.
  • API는 CORS를 허용하여 안전하지 않습니다.
    • CORS를 적용하는 것은 클라이언트(브라우저)의 입니다. 서버는 요청을 실행하고 응답을 반환하며, 오류를 반환하고 응답을 차단하는 클라이언트입니다. 예를 들어 다음 도구는 서버 응답을 표시합니다.
  • 서버에서 브라우저가 원본 간 XHR 또는 Fetch API 요청을 실행할 수 있도록 허용하는 방법이며, 그렇지 않으면 사용할 수 없습니다.
    • CORS가 없는 브라우저는 원본 간 요청을 수행할 수 없습니다. CORS 이전에는 JSONP를 사용하여 이 제한을 피했습니다. JSONP는 XHR을 사용하지 않고 <script> 태그를 사용하여 응답을 받습니다. 스크립트는 원본 간 로드할 수 있습니다.

CORS 사양에는 원본 간 요청을 사용하도록 설정하는 몇 가지 새로운 HTTP 헤더가 도입되었습니다. 브라우저에서 CORS를 지원하는 경우 원본 간 요청에 대해 이러한 헤더를 자동으로 설정합니다. 사용자 지정 JavaScript 코드는 CORS를 사용하도록 설정하는 데 필요하지 않습니다.

배포된 샘플의 PUT 테스트 단추

다음은 테스트 단추에서 로의 원본 간 요청 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 원본 간 요청이 실패합니다. 특히 브라우저는 요청을 허용되지 않습니다. 서버가 성공적인 응답을 반환하더라도 브라우저는 클라이언트 앱에서 응답을 사용할 수 있도록 설정하지 않습니다.

옵션 요청 표시

기본적으로 Chrome 및 Edge 브라우저는 F12 도구의 네트워크 탭에 옵션 요청을 표시하지 않습니다. 이러한 브라우저에서 OPTIONS 요청을 표시하려면 다음을 수행합니다.

  • chrome://flags/#out-of-blink-cors 또는 edge://flags/#out-of-blink-cors
  • 플래그를 사용하지 않도록 설정합니다.
  • 다시 시작할.

Firefox는 기본적으로 OPTIONS 요청을 표시합니다.

IIS의 CORS

IIS에 배포할 때 서버가 익명 액세스를 허용하도록 구성되지 않은 경우 인증을 Windows 전에 CORS를 실행해야 합니다. 이 시나리오를 지원하려면 앱에 대해 IIS CORS 모듈을 설치하고 구성해야 합니다.

CORS 테스트

샘플 다운로드에는 CORS를 테스트하는 코드가 있습니다. 다운로드하는 방법을 참조하세요. 샘플은 Pages가 추가된 API 프로젝트입니다. Razor

public class StartupTest2
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: "MyPolicy",
                builder =>
                {
                    builder.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을 사용하여 에서 샘플을 dotnet run https://localhost:5001 실행합니다.
  • 의 URL에 대해 포트가 44398로 설정된 Visual Studio 샘플을 https://localhost:44398 실행합니다.

F12 도구와 함께 브라우저 사용:

  • 단추를 선택하고 네트워크 탭에서 헤더를 검토합니다.

  • PUT 테스트 단추를 선택합니다. OPTIONS 요청을 표시하는 방법에 대한 지침은 옵션 요청 표시를 참조하세요. PUT 테스트는 두 개의 요청, 즉 OPTIONS 사전 준비 요청과 PUT 요청을 만듭니다.

  • 실패한 GetValues2 [DisableCors] CORS 요청을 트리거하는 단추를 선택합니다. 문서에서 설명한 대로 응답은 200개의 성공을 반환하지만 CORS 요청은 만들어지지 않습니다. 콘솔 탭을 선택하여 CORS 오류를 확인합니다. 브라우저에 따라 다음과 유사한 오류가 표시됩니다.

    원본에서 가져오기에 대한 'https://cors1.azurewebsites.net/api/values/GetValues2' 'https://cors3.azurewebsites.net' 액세스가 CORS 정책에 의해 차단되었습니다. 요청된 리소스에 'Access-Control-Allow-Origin' 헤더가 없습니다. 사용자 요구에 불명확한 응답이 제공되면 요청 모드를 'no-cors'로 설정하여 CORS가 사용되지 않도록 설정된 리소스를 가져옵니다.

CORS 사용 엔드포인트는 curl, Fiddler또는 Postman와 같은 도구를 사용하여 테스트할 수 있습니다. 도구를 사용하는 경우 헤더에 지정된 요청의 원본이 Origin 요청을 수신하는 호스트와 달라야 합니다. 헤더 값에 따라 요청이 원본 간이 아닌 Origin 경우:

  • CORS 미들웨어가 요청을 처리할 필요가 없습니다.
  • CORS 헤더는 응답에 반환되지 않습니다.

다음 명령은 를 사용하여 curl 정보로 OPTIONS 요청을 실행합니다.

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

엔드포인트 라우팅 및 [HttpOptions]를 통해 CORS 테스트

현재 를 사용하여 엔드포인트별로 CORS를 사용하도록 RequireCors 설정해도 자동 작동 전 요청은 지원되지 않습니다. 엔드포인트 라우팅을 사용하여 CORS를 사용하도록 설정하는다음 코드를 고려합니다.

public class StartupEndPointBugTest
{
    readonly string MyPolicy = "_myPolicy";

    // .WithHeaders(HeaderNames.ContentType, "x-custom-header")
    // forces browsers to require a preflight request with GET

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyPolicy,
                builder =>
                {
                    builder.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]GET [EnableCors] 단추는 성공합니다. 엔드포인트에 및 가 [EnableCors] 있고 전등 요청에 응답하기 때문입니다. 다른 엔드포인트는 실패합니다. JavaScript에서 다음을 보내기 때문에 GET 단추가 실패합니다.

 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를 선택한 다음, 컨트롤러를 설정합니다. 엔드포인트에 대한 모든 CORS TodoItems2Controller 호출이 성공합니다.

추가 리소스