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 자동 실행 전 요청을 지원 하지 않습니다. 자세한 내용은 이 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 정책을정의 하지 않습니다.
  • [EnableCors ("MyPolicy")] 를 사용 하 여 "MyPolicy" 컨트롤러에 대 한 CORS 정책을 사용 하도록 설정 합니다.
  • 메서드에 대해 CORS를 사용 하지 않도록 설정 GetValues2 합니다.

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

CORS 정책 옵션

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

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

허용되는 원본 설정

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

참고

및 를 지정하는 AllowAnyOrigin AllowCredentials 것은 안전하지 않은 구성이며 사이트 간 요청 위조가 발생할 수 있습니다. 앱이 두 가지 방법으로 구성된 경우 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 요청 섹션을 참조하세요.

로 지정 된 특정 헤더와 일치 하는 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 based: ' ' 원본에서 ' '의 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는 사용 전 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 표준입니다.
  • 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

앞의 응답 헤더에서 서버는 응답에서 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/ 합니다. 샘플을 다운로드할 필요가 없습니다.
  • 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 정보로 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 호출이 성공합니다.

추가 자료

작성자: Rick Anderson

이 문서에서는 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: 다른 포트

Internet Explorer는 원본을 비교할 때 포트를 고려 하지 않습니다.

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

CORS 미들웨어는 크로스-원본 요청을 처리 합니다. 다음 코드는 지정 된 원본으로 전체 앱에 대해 CORS를 사용 하도록 설정 합니다.

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public IConfiguration Configuration { get; }

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

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }

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

        app.UseCors(MyAllowSpecificOrigins); 

        app.UseHttpsRedirection();
        app.UseMvc();
    }
}

위의 코드는

  • 정책 이름을 " _ myAllowSpecificOrigins"로 설정 합니다. 정책 이름은 임의로입니다.
  • UseCorsCORS를 사용 하도록 설정 하는 확장 메서드를 호출 합니다.
  • AddCors 람다 식을통해 를 호출합니다. 람다는 CorsPolicyBuilder 개체를 가져옵니다. 같은 구성 옵션은 WithOrigins 이 문서의 후반부에서 설명합니다.

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

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

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

자세한 내용은 이 문서의 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.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

참고: URL은 후행 슬래시( )를 포함해서는 안됩니다. / URL이 로 종료되면 / 비교가 false 반환되고 헤더가 반환되지 않습니다.

다음 코드는 CORS 미들웨어를 통해 모든 앱 엔드포인트에 CORS 정책을 적용합니다.

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

    app.UseCors();

    app.UseHttpsRedirection();
    app.UseMvc();
}

참고: 전에 UseCors 를 호출해야 UseMvc 합니다.

Razor 페이지/컨트롤러/작업 수준에서 CORS 정책을 적용하려면 Pages, 컨트롤러 및 작업 메서드에서 CORS 사용을 참조하세요.

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

특성으로 CORS 사용

[ EnableCors ] 특성은 CORS를 전역적으로 적용하는 대안을 제공합니다. [EnableCors]특성은 모든 끝점이 아닌 선택한 끝점에 대해 CORS를 사용하도록 설정합니다.

를 사용하여 [EnableCors] 기본 정책을 지정하고 [EnableCors("{Policy String}")] 정책을 지정합니다.

[EnableCors]특성은 다음 에 적용할 수 있습니다.

  • Razor 페이지 PageModel
  • 컨트롤러
  • 컨트롤러 작업 메서드

특성을 사용하여 controller/page-model/action에 다른 정책을 적용할 수 [EnableCors] 있습니다. [EnableCors]특성이 컨트롤러/페이지 모델/동작 메서드에 적용 되 고 CORS가 미들웨어에서 사용 하도록 설정 된 경우 두 정책이 모두 적용 됩니다. 정책을 결합 하지 않는 것이 좋습니다. [EnableCors]특성 또는 미들웨어를 사용 합니다 . 를 사용 하 [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]        // Default policy.
    [HttpGet("{id}")]
    public ActionResult<string> Get(int id)
    {
        switch (id)
        {
            case 1:
                return "green widget";
            case 2:
                return "red widget";
            default:
                return NotFound();
        }
    }
}

다음 코드에서는 CORS 기본 정책과 라는 정책을 만듭니다 "AnotherPolicy" .

public class StartupMultiPolicy
{
    public StartupMultiPolicy(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

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

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

        });

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }

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

        app.UseHttpsRedirection();
        app.UseMvc();
    }
}

CORS 사용 안 함

[ DisableCors ] 특성은 컨트롤러/페이지-모델/작업에 대해 CORS를 사용 하지 않도록 설정 합니다.

CORS 정책 옵션

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

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

허용 되는 원본 설정

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

참고

및 를 지정하는 AllowAnyOrigin AllowCredentials 것은 안전하지 않은 구성이며 사이트 간 요청 위조가 발생할 수 있습니다. 보안 앱의 경우 클라이언트가 서버 리소스에 액세스할 수 있는 권한을 부여해야 하는 경우 정확한 원본 목록을 지정합니다.

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

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

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

허용되는 HTTP 메서드 설정

AllowAnyMethod:

허용되는 요청 헤더 설정

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

options.AddPolicy("AllowHeaders",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .WithHeaders(HeaderNames.ContentType, "x-custom-header");
    });

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

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

이 설정은 준비 요청 및 헤더에 영향을 Access-Control-Request-Headers 미칩니다. 자세한 내용은 Preflight requests 섹션을 참조하세요.

CORS 미들웨어는 Access-Control-Request-Headers CorsPolicy.Headers에 구성된 값에 관계없이 의 4개 헤더를 항상 보낼 수 있습니다. 이 헤더 목록에는 다음이 포함됩니다.

  • Accept
  • Accept-Language
  • Content-Language
  • Origin

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

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

CORS 미들웨어는 가 항상 허용되므로 다음 요청 헤더를 사용하여 수행 전 요청에 성공적으로 Content-Language 응답합니다.

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

노출된 응답 헤더 설정

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

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

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

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

options.AddPolicy("ExposeResponseHeaders",
    builder =>
    {
        builder.WithOrigins("http://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("AllowCredentials",
    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 않습니다.

다음은 수행 전 요청의 예입니다.

OPTIONS https://myservice.azurewebsites.net/api/test HTTP/1.1
Accept: */*
Origin: https://myclient.azurewebsites.net
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: accept, x-my-custom-header
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Host: myservice.azurewebsites.net
Content-Length: 0

사전 요청은 HTTP OPTIONS 메서드를 사용합니다. 여기에는 다음과 같은 두 가지 특수 헤더가 포함됩니다.

  • Access-Control-Request-Method: 실제 요청에 사용할 HTTP 메서드입니다.
  • Access-Control-Request-Headers: 앱이 실제 요청에 대해 설정하는 요청 헤더 목록입니다. 앞서 언급했듯이 브라우저에서 설정하는 헤더(예: )는 포함되지 User-Agent 않습니다.

적절한 정책을 사용하여 CORS를 사용하도록 설정하면 ASP.NET Core는 일반적으로 CORS 사전 요청에 자동으로 응답합니다. 사전 요청은 [HttpOptions] 특성을 참조하세요.

CORS 준비 요청에는 실제 Access-Control-Request-Headers 요청과 함께 전송되는 헤더를 서버에 나타내는 헤더가 포함될 수 있습니다.

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

options.AddPolicy("AllowHeaders",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .WithHeaders(HeaderNames.ContentType, "x-custom-header");
    });

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

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

브라우저의 설정 방법은 완전히 일치 하지 않습니다 Access-Control-Request-Headers . 헤더를 (또는을 사용 하는 경우) 이외의 값으로 설정 하는 경우, "*" AllowAnyHeader Accept 및를 포함 하 Content-Type 고 지원 하려는 사용자 지정 헤더를 포함 해야 합니다 Origin .

다음은 실행 전 요청에 대 한 응답 예제입니다 (서버에서 요청을 허용 한다고 가정).

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 0
Access-Control-Allow-Origin: https://myclient.azurewebsites.net
Access-Control-Allow-Headers: x-my-custom-header
Access-Control-Allow-Methods: PUT
Date: Wed, 20 May 2015 06:33:22 GMT

응답에는 허용 된 Access-Control-Allow-Methods 메서드를 나열 하는 헤더와 허용 되는 헤더를 나열 하는 헤더 (선택 사항)가 포함 되어 있습니다 Access-Control-Allow-Headers . 실행 전 요청이 성공 하면 브라우저에서 실제 요청을 보냅니다.

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

실행 전 만료 시간 설정

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

options.AddPolicy("SetPreflightExpiration",
    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를 지원하는 경우 원본 간 요청에 대해 이러한 헤더를 자동으로 설정합니다. 사용자 지정 JavaScript 코드는 CORS를 사용하도록 설정하는 데 필요하지 않습니다.

다음은 원본 간 요청의 예입니다. Origin헤더는 요청을 만드는 사이트의 도메인을 제공합니다. Origin헤더는 필수이며 호스트와 달라야 합니다.

GET https://myservice.azurewebsites.net/api/test HTTP/1.1
Referer: https://myclient.azurewebsites.net/
Accept: */*
Accept-Language: en-US
Origin: https://myclient.azurewebsites.net
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Host: myservice.azurewebsites.net

서버에서 요청을 허용하면 Access-Control-Allow-Origin 응답에 헤더가 설정됩니다. 이 헤더의 값은 Origin 요청의 헤더와 일치하거나 와일드카드 값 입니다. "*" 즉, 모든 원본이 허용됩니다.

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/plain; charset=utf-8
Access-Control-Allow-Origin: https://myclient.azurewebsites.net
Date: Wed, 20 May 2015 06:27:30 GMT
Content-Length: 12

Test message

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

CORS 테스트

CORS를 테스트하려면 다음을 수행합니다.

  1. API 프로젝트 를 만듭니다. 또는 샘플을 다운로드할수 있습니다.
  2. 이 문서의 방법 중 하나를 사용 하 여 CORS를 사용 하도록 설정 합니다. 예를 들어:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseHsts();
    }

    // Shows UseCors with CorsPolicyBuilder.
    app.UseCors(builder =>
    {
        builder.WithOrigins("http://example.com",
                            "http://www.contoso.com",
                            "https://localhost:44375",
                            "https://localhost:5001");
    });

    app.UseHttpsRedirection();
    app.UseMvc();
}

경고

WithOrigins("https://localhost:<port>");다운로드 샘플 코드와 유사한 샘플 앱을 테스트 하는 데만 사용 해야 합니다.

  1. 웹 앱 프로젝트 ( Razor 페이지 또는 MVC)를 만듭니다. 이 샘플에서는 Razor 페이지를 사용 합니다. API 프로젝트와 동일한 솔루션에서 웹 앱을 만들 수 있습니다.
  2. 다음 강조 표시 된 코드를 Index. cshtml 파일에 추가 합니다.
@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="text-center">
    <h1 class="display-4">CORS Test</h1>
</div>

<div>
    <input type="button" value="Test" 
           onclick="requestVal('https://<web app>.azurewebsites.net/api/values')" />
    <span id='result'></span>
</div>

<script>
    function requestVal(uri) {
        const resultSpan = document.getElementById('result');

        fetch(uri)
            .then(response => response.json())
            .then(data => resultSpan.innerText = data)
            .catch(error => resultSpan.innerText = 'See F12 Console for error');
    }
</script>
  1. 위의 코드에서를 배포 된 url: 'https://<web app>.azurewebsites.net/api/values/1', 앱의 URL로 바꿉니다.

  2. API 프로젝트를 배포 합니다. 예를 들어 Azure에 배포할 수있습니다.

  3. Razor바탕 화면에서 페이지 또는 MVC 앱을 실행 하 고 테스트 단추를 클릭 합니다. F12 도구를 사용 하 여 오류 메시지를 검토 합니다.

  4. 에서 localhost 원본을 제거 WithOrigins 하 고 앱을 배포 합니다. 또는 다른 포트를 사용 하 여 클라이언트 앱을 실행 합니다. 예를 들어 Visual Studio에서를 실행 합니다.

  5. 클라이언트 앱을 사용 하 여 테스트 합니다. CORS 오류는 오류를 반환 하지만 JavaScript에는 오류 메시지를 사용할 수 없습니다. F12 도구에서 콘솔 탭을 사용 하 여 오류를 확인 합니다. 브라우저에 따라 다음과 비슷한 오류가 표시 됩니다 (F12 도구 콘솔).

    • Microsoft Edge 사용:

      SEC7120: [CORS] 원본이 에서 https://localhost:44375 https://localhost:44375 원본 간 리소스에 대한 Access-Control-Allow-Origin 응답 헤더에서 찾을 수 없습니다. https://webapi.azurewebsites.net/api/values/1

    • Chrome 사용:

      원본에서 XMLHttpRequest에 대한 https://webapi.azurewebsites.net/api/values/1 https://localhost:44375 액세스가 CORS 정책에 의해 차단되었습니다. 요청된 리소스에 'Access-Control-Allow-Origin' 헤더가 없습니다.

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

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

IIS의 CORS

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

추가 자료