What is CORS?

무네·2022년 1월 28일
0
post-thumbnail
post-custom-banner

CORS란 무엇인가

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

예)
https://domain-a.com 의 프론트엔드 JavaScript 코드가 XMLHttpRequest를 사용하여 https://domain-b.com/data.json 을 요청하는 경우

Cross-Origin HTTP 요청을 실행하면 브라우저는 보안 상의 이유로 요청을 제한한다. Fetch API의 경우 동일 출처 정책을 따르고 있어서 Fetch API를 사용하는 웹 애플리케이션은 자신의 출처와 동일한 리소스만 불러올 수 있으며, 다른 출처의 리소스를 불러오려면 그 출처에서 올바른 CORS 헤더를 포함한 응답을 반환해야 한다.

즉, CORS는 브라우저와 서버 간 안전한 교차 출처 요청 및 데이터 전송을 지원한다.

CORS의 세 가지 동작 방식

1. 단순 요청 (Simple Request)

<JavaScript 코드>

const xhr = new XMLHttpRequest();
const url = 'https://bar.other/resources/public-data/`;

xhr.open('GET', url);
xhr.onreadystatechange = someHandler;
xhr.send();

<요청 헤더>
Origin: https://foo.example 를 통해 어디로부터 요청이 왔는지 확인 가능

GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example

<서버 응답>
Access-Control-Allow-Origin: *는 모든 도메인에서 접근할 수 있음을 의미한다. 만약 특정 도메인만 허용하려면 특정 도메인을 값으로 적는다.

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

[…XML Data…]

2. Preflight 요청

preflighted는 1번의 단순 요청과 달리 HTTP의 OPTIONS 메소드를 통해 다른 도메인의 리소스로 HTTP 요청을 미리 보내 실제 요청이 과연 안전한지 확인한다.

<JavaScript 코드>
사용자 정의 헤더가 설정되었기 때문에 preflighted 처리된다.

const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://bar.other/resources/post-here/');
xhr.setRequestHeader('Ping-Other', 'pingpong');
xhr.setRequestHeader('Content-Type', 'application/xml');
xhr.onreadystatechange = handler;
xhr.send('<person><name>Arun</name></person>');

<preflight 요청/응답>

OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type


HTTP/1.1 204 No Content
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
  • 1~10행은 OPTIONS 메소드를 사용한 preflighted request를 나타낸다.
    OPTIONS는 서버에서 추가 정보를 판별하는데 사용하는 HTTP/1.1 메소드로, safe 메소드이기 때문에 리소스를 변경하는 데에는 사용할 수 없다.

  • OPTIONS 요청과 함께 두 개의 요청 헤더가 전송된다.
    Access-Control-Request-Method: POST
    : preflight request의 일부로, 실제 요청을 전송할 때 POST로 전송된다는 것을 알려줌
    Access-Control-Request-Headers: X-PINGOTHER, Content-Type
    : 실제 요청을 전송할 때 X-PINGOTHER과 Content-Type 사용자 정의 헤더와 함께 전송된다는 것을 알려줌
    -> 이제 서버는 이러한 상황에서 요청을 수락할지 결정할 수 있음

  • 13~22행은 서버가 요청 메소드와 요청 헤더를 받을 수 있음을 나타내는 응답이다.
    Access-Control-Allow-Methods: POST, GET, OPTIONS
    : POST와 GET이 리소스를 쿼리하는데 유용한 메서드라고 알려줌
    Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
    : 실제 요청에 이 헤더를 사용할 수 있음을 확인해 줌
    Access-Control-Max-Age
    : 다른 preflight request를 보내지 않고 preflight request에 대한 응답을 캐시할 수 있는 시간(초) 제공, 각 브라우저의 최대 캐싱 시간은 해당 값이 클수록 우선순위가 높음

<preflight request가 완료된 뒤의 실제 요청/응답>

POST /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: https://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: https://foo.example
Pragma: no-cache
Cache-Control: no-cache

<person><name>Arun</name></person>


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain

[Some GZIP'd payload]

3. 인증정보를 포함한 요청

인증정보를 포함한 요청은 HTTP 쿠키와 HTTP Authentication 정보를 인식한다. 기본적으로 Fetch 호출에서 브라우저는 자격 증명을 보내지 않으며, Request 생성자가 호출될 때 특정 플래그를 설정해야 한다.

<JavaScript 코드>

const invocation = new XMLHttpRequest();
const url = 'http://bar.other/resources/credentialed-content/';

function callOtherDomain() {
  if (invocation) {
    invocation.open('GET', url, true);
    invocation.withCredentials = true;
    invocation.onreadystatechange = handler;
    invocation.send();
  }
}
  • withCredentials: 쿠키와 함께 호출하기 위한 플래그로, boolean값임

<요청/응답>

GET /resources/credentialed-content/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Referer: http://foo.example/examples/credential.html
Origin: http://foo.example
Cookie: pageAccess=2


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:34:52 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 106
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain


[text/plain payload]
  • 10행에서 Cookie를 포함해서 요청을 보냈어도 응답할 때 Access-Control-Allow-Credentials: true로 응답하지 않으면 웹 컨텐츠는 제공되지 않는다.

  • 헤더에 Cookie가 포함되어 있는 자격 증명 요청에 응답할 때 서버는 Access-Control-Allow-Origin에 출처를 지정해야 한다. 와일드카드(*)를 지정해서는 안 됨.



References

https://developer.mozilla.org/ko/docs/Web/HTTP/CORS

profile
궁금한 건 정말 참을 수 없어
post-custom-banner

0개의 댓글