CS

CORS(Cross Origin Resource Sharing)

jungmin.park 2024. 1. 2. 19:19
CORS(Cross Origin Resource Sharing)에 대해 설명해주세요.
CORS란 교차 출처 리소스 공유로 추가 HTTP 헤더를 사용하여, 한 Origin에서 실행 중인 웹 어플리케이션이 다른 Origin의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에서 알려주는 체제입니다. 웹어플리케이션은 리소스가 자신의 Origin과 다를 때 COR HTTP 요청을 실행합니다. CORS는 HTTP요청을 보낼때 요청 헤더에 Origin 담아 보냅니다. 서버는 응답해더에 Access-Control-Allow-Origin을 담아 클라이언트로 전달합니다. 이때 Origin을 비교하는 로직은 브라우저에 구현이 되어있기 때문에 서버의 응답은 CORS정책 위반 여부에 관여하지 않습니다. 클라이언트에서는 자신이 보냈던 요청의 Origin과 서버가 보내준 Access-Control-Allow-Origin을 비교하여 차단을 할지말지 결정합니다. 만약 유효하지 않다면 그 응답을 사용하지 않고 버립니다. CORS 작동방식으로 예비요청, 단순요청, 인증정보를 포함한 요청이 있습니다. 예비요청은 본 요청을 보내기 전 브라우저 스스로 안전한 요청인지 확인하는 것으로 메소드는 OPTIONS요청이 사용됩니다. 예비요청의 장점은 실제 요청을 보내기 전 미리 권한을 확인할 수 있기 때문에 리소스 측면에서 효율적입니다. 단순요청은 예비요청을 보내지 않고 바로 서버에 직행으로 본 요청을 보낸 후, 서버가 이에 대한 응답의 헤더에 Access-Control-Allow-Origin과 같은 값을 보내주면 브라우저가 CORS정책 위반 여부를 검사하는 방식입니다. 하지만 단순요청은 3가지 조건을 만족해야 합니다. GET, HEAD, POST 요청 중 하나여야 하며 자동으로 설정된 헤더외에 Accept, Accept-Language, Content-Language, Content-Type 헤더의 값만 수동으로 설정할 수 있습니다. Content-Type헤더에는 application/x-www-form-urlencoded multipart/form-data text/plain 값만 허용됩니다. POST요청의 경우 대부분 application/json으로 통신하기 때문에 Content-Type에 위반되어 대부분 예비요청으로 이루어집니다. 인증정보를 포함한 요청은 요청 헤더에 인증 정보를 담아 보내는 방식으로 인증과 관련된 정보를 credentials 옵션값을 설정하고 서버로 보내게 됩니다. Credentials 옵션을 사용하여 요청에 인증정보가 담겨있는 상태에서 다른 출처의 리소스를 요청하게 되면 브라우저는 CORS정책 위반 여부를 검사하는 룰에 다음 두가지를 추가하여 검사합니다. *Access-Control-Allow-Origin 에는 모든 요청을 허용하는 * 을 사용할 수 없으며, 명시적인 URL이여야 한다. * 응답헤더는 반드시 Access-Control-Allow-Credentails :true가 존재해야 한다. 결국 인증된 요청 역시 예비요청이 먼저 일어나게 되는 것입니다.

 

 

CORS란

CORS는 Cross-Origin Resource Sharing의 줄임말로 '교차 출처 리소스 공유' 라고 해석한다.

추가 HTTP 헤더를 사용하여, 한 Origin에서 실행 중인 웹 어플리케이션이 다른 Origin의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에서 알려주는 체제이다. 웹 어플리케이션은 리소스가 자신의 Origin(도메인, 프로토콜, 포트)와 다를 때 교차 출처 HTTP 요청을 실행한다.

 

Origin/ Cross-Origin 이란 무엇인가?

Origin과 비슷한 개념으로 도메인(domain)이 있다.

  • 도메인(domain) : naver.com
  • 오리진(origin): https://www.naver.com/PORT

Origin의 URL구조에서 살펴본 Protocol, Host, Port를 합친 것

 

SOP (Same Origin Policy)

SOP는 같은 출처에서만 리소스를 공유할 수 있다라는 규칙을 정한 정책이다.

즉 같은 프로토콜, 호스트, 포트를 사용한다면 다른 요소는 다르더라도 같은 Origin로 인정된다.

반대로 리소스가 자신의 출처와 다를경우 브라우저는 Cross Origin(교차출처)를 요청한다.

 

Origin를 비교하는 로직은 서버에서 구현된 스펙이 아닌 브라우저에서 구현된 스펙이다.

만약 CORS정책을 위반하는 요청에 서버가 정상적으로 응답을 하더라도 브라우저가 이 응답을 분석해서 CORS정책에 위반되면 그 응답은 처리하지 않게 된다.

정리하자면 프로토콜, 호스트, 포트 하나라도 일치하지 않으면 Cross Origin이 된다.

표를 보고 이것은 SOP인지 COR인지 비교해보자. 

 

예) https://etloveguitar.tistory.com

 

 

다시 한 번 CORS에 대한 개념을 상기시켜보자.

 

추가 HTTP 헤더를 사용하여, 한 Origin에서 실행 중인 웹 어플리케이션이 다른 Origin의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에서 알려주는 체제이다. 

 

CORS 에러는 CORS를 허용해서 아무런 문제 없이 다른 Origin 리소스를 공유해달라는 권고사항 같은 것이다.

 

CORS 기본 동작과정

1. 클라이언트에서 HTTP요청의 헤더에 Origin을 담아 전달한다.

다른 Origin의 리소스 요청시 클라이언트는 HTTP요청을 보낸다.

이 때 요청헤더의 Origin필드에는 요청을 보내는 Origin을 담아 보낸다.

 

2. 서버는 응답헤더에 Access-Control-Allow-Origin을 담아 클라이언트로 전달한다.

서버가 응답을 보낼때, 허락하는 Origin을 클라이언트에게 전달한다.

3. 클라이언트에서, 자신이 보냈던 요청의 Origin과 서버가 보내준 Access-Control-Allow-Origin을 비교한다.

자신이 보낸 Origin과 서버가 보내준 Access-Control-Allow-Origin을 비교하여 차단할지 말지를 결정한다.

만약 유효하지 않다면, 그 응답을 사용하지 않고 버린다.

서버의 응답은 CORS정책 위반 여부에 관여하지 않는다.
CORS정책에 의해 Origin을 비교하는 로직은 브라우저에 구현되어 있다.
그래서 서버에서 정상적인 응답을 하여 상태코드가 200이 나오더라도 브라우저가 응답을 CORS정책 위반이라고 분석하면 그 응답은 사용하지 않는다.
브라우저가 CORS정책 위반을 분석하는 시간은 서버의 응답이 도착한 이후이다.
즉, CORS정책을 위반하는 리소스 요청때문에 에러가 발생하더라도 서버 쪽 로그에서는 정상응답을 했다는 로그만 남기 때문에, CORS를 정확히 이해해야만 CORS에러를 해결할 수 있다.

 

CORS 작동방식

 

1. 예비요청(Preflight Request)

브라우저는 요청을 한번에 보내지 않고, 예비요청과 본 요청을 나누어 서버에 전달한다.

이때 브라우저가 예비요청을 보내는 것을 preflight라고 부르며, 이 예비요청의 메소드는 GET이나 POST가 아닌 OPTIONS라는 요청이 사용된다.

예비요청의 역할은 본 요청을 보내기 전에 브라우저 스스로 안전한 요청인지 확인하는 것이다.

 

  • 브라우저는 서버로 예비요청을 먼저 보낸다.
  • 서버는 이 예비요청에 대한 응답으로 어떤 것을 허용하고 어떤 것을 금지하고 있는지에 대한 정보를 담아서 브라우저에게 다시 보내준다.
  • 이후 브라우저는 보낸 요청과 서버가 응답해준 정책을 비교하여 해당 요청이 안전한지 확인하고 본 요청을 보내게 된다.
  • 이후 서버가 본 요청에 대한 응답을 하면 최종적으로 이 응답 데이터를 자바스크립트로 넘겨준다.
  • 만약 요청을 보낸 출처가 접근 권한이 없다면 브라우저에서 CORS 에러를 띄우게 되고, 실제 요청은 전달하지 않는다.

예비요청(Preflight Request) 장점

  • 실제 요청을 보내기 전에 미리 권한 확인을 할 수 있기 때문에, 실제 요청을 처음부터 통째로 보내는 것보다 리소스 측면에서 효율적이다.
  • CORS에 대비가 되어있지 않는 서버를 보호할 수 있다. CORS 이전에 만들어진 서버들은 SOP 요청만 들어오는 상황을 고려하고 만들었기 때문에 CORS에 대한 대비가 되어있지 않다.

2. 단순 요청(Simple Request)

단순요청은 예비요청을 보내지않고 바로 서버에 직행으로 본 요청을 보낸 후, 서버가 이에 대한 응답의 헤더에 Access-Control-Allow-Origin과 같은 값을 보내주면 브라우저가 CORS정책 위반 여부를 검사하는 방식이다.

 

 

단순요청은 예비요청을 생략하는 뜻인데 3가지 조건을 만족해야만 가능하다.

조건

  • GET, HEAD, POST 요청 중 하나여야 한다.
  • 자동으로 설정되는 헤더 외에, Accept, Accept-Language, Content-Language, Content-Type  헤더의 값만 수동으로 설정할 수 있다.
  • Content-Type 헤더에는 application/x-www-form-urlencoded multipart/form-data text/plain 값만 허용된다.

위의 조건으로 인해 단순요청이 일어나는 상황을 만드는 것은 쉽지 않다.

POST요청은 대부분 application/json으로 통신하기 때문에 Content-Type에 위반된다. 

결국 대부분 예비요청으로 이루어진다고 생각하면 된다.

 

3. 인증정보를 포함한 요청(Credentialed Request)

요청 헤더에 인증 정보를 담아 보내는 방식이다. 기존 예비 요청에서 보안을 더 강화하고 싶을때 사용한다.

출처가 다를 경우에는 별도의 설정을 하지 않으면 쿠키를 보낼 수 없는데 이것과 관련된 옵션이 있다.

인증과 관련된 정보(쿠키)를 담을 수 있게 해주는 옵션이 있는데 Credentials 옵션이다. 이 옵션은 총 3가지 값을 사용할 수 있다.

same-origin(기본값) 같은 출처 간 요청에만 인증 정보를 담을 수 있다.
include 모든 요청에 인증 정보를 담을 수 있다
omit 모든 요청에 인증 정보를 담지 않는다

만약 Credentials 옵션값인 same-origin이나 include와 같은 옵션을 사용하여 리소스 요청에 인증정보가 포함된다면, 브라우저는 다른 Origin의 리소스를 요청할 때 Access-Control-Allow-Origin만 확인하는 것이 아니라 다른 조건을 추가로 검사하게 된다.

 

예를 들어, Credentials 옵션을 사용하여 요청에 인증정보가 담겨있는 상태에서 다른 출처의 리소스를 요청하게 되면 브라우저는 CORS정책 위반 여부를 검사하는 룰에 다음 두가지를 추가하게 된다.

  • Access-Control-Allow-Origin 에는 모든 요청을 허용하는 * 을 사용할 수 없으며, 명시적인 URL이여야 한다.
  • 응답헤더는 반드시 Access-Control-Allow-Credentails :true가 존재해야 한다.

결국 인증된 요청 역시 Preflight 요청이 먼저 일어난다.

 

 

Reference


https://velog.io/@dae_eun2/CORS

https://etloveguitar.tistory.com/83

https://velog.io/@dae_eun2/CORS

https://velog.io/@sangbin2/CORS-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D