[Network] HTTP CORS
본 내용은 MDN HTTP CORS를 보고 공부, 정리한 내용입니다.
CORS 개념을 알기 위해서는 먼저 브라우저의 SOP (Same Origin Policy) 에 대해서 알아야 한다.
동일 출처 정책 (SOP)
자바스크립트 엔진 표준 스펙에 정의되어 있는 이 정책은 스크립트의 요청 (대표적으로 XMLHttpRequest를 사용한 Ajax) 은 동일한 출처의 서버에게 요청할 때만 그 요청에 대한 응답을 확인할 수 있다는 내용을 담고 있다. 여기서 말하는 출처란 프로토콜, 호스트명(도메인), 포트를 말한다.
요약하자면, 클라이언트가 서버A로부터 받아온 js 파일에서 서버B에 대한 리소스 요청을 할 때, 브라우저는 현재 자신의 Origin인 서버A와 js파일이 요청을 보내고자 하는 서버B가 서로 다른 출처임을 파악하고 서버B로부터 전달받은 응답 결과를 차단한다.
SOP가 어느 상황에 발생하는지를 아래 그림이 잘 설명해준다.
출처 : MDN HTTP CORS
클라이언트가 domain-a.com 에 접속하면 웹 브라우저는 domain-a.com 서버 (이하 서버A) 의 html, css, js 등을 받아오게 된다. 이 때 받아온 js 파일에서 domain-b.com 서버 (이하 서버B) 의 리소스를 요청하게 되면, 서버B는 해당 리소스를 담아 응답해주지만 웹 브라우저는 현재 자신의 Origin이 서버A이므로 서버B로부터 온 응답 리소스를 로딩하지 않는다.
- 참고로 Origin 헤더에는 경로 없이 서버 도메인 이름만 정의되며, null이나 URI가 올 수도 있다.
SOP는 무분별한 http 데이터 교환을 방지하기 위해 만들어졌으며, 해결 방법에는 여러 가지가 있다.
1. 클라이언트단 해결방법
1-1. 웹 브라우저에서 동일 출처 정책 확인 off
1-2. JSONP 방식으로 요청.
웹 브라우저에서 css, js 파일은 SOP의 영향 없이 로딩이 가능한 점을 이용하여 서버 응답을 JSON 형태로 받아온다. 단, GET 요청만 가능하다.
2. 서버단 해결방법
클라이언트 단에서의 SOP 우회 방법은 범용적일 수 없다. 이에 서버단에서 SOP문제를 해결하는 방법이 나왔는데, 이것이 CORS이다.
교차 출처 리소스 공유
CORS는 SOP를 해결하기 위해 등장한 정책이다. 다른 말로 서버와 클라이언트가 헤더를 보고 서로의 요청 혹은 응답에 반응할지 말지를 결정하는 프로세스이다. 간단히 말해서 Cross-Site Http Request를 허용해주기 위한 방법이라고 생각해도 좋을 듯 하다.
MDN에서는 CORS가 적용되는 3가지 시나리오를 다루고 있다. 설명하면서 예시를 들어야 할 땐 SOP를 이야기할 때 사용했던 예시상황을 토대로 설명하겠다.
1. Simple requests
클라이언트가 아래 조건을 충족하는 Cross-Site Http Request를 보낼 때, 서버측에서는 CORS 정책에 따라 응답 헤더에 Access-Control-Allow-Origin 을 추가하여 클라이언트에게 보낸다.
조건을 요약하면 아래와 같다.
- GET, HEAD, POST 중 하나의 메서드여야 한다.
- POST 메서드 사용 시, Content-type은 아래 중 하나여야 한다.
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
- 커스텀 헤더가 사용되서는 안된다.
(자세한 조건은 여기를 클릭해서 확인할 수 있다.)
1 |
|
클라이언트의 Simple request에 대해 서버가 Access-Control-Allow-Origin 헤더를 포함하여 응답하면, 웹 브라우저는 자신의 Origin이 서버의 Access-Control-Allow-Origin 헤더에 포함된 사이트인지를 확인한 후 사용자에게 해당 리소스를 로딩할지 결정한다.
2. Preflight requests
웹 브라우저가 OPTIONS 메서드를 사용하여 외부 서버에게 자신이 해당 리소스를 가져올 권한이 있는지 확인하는 방법이다.
Simple request 조건에 포함되지 않는 Cross-Site Http Request는 자동으로 Preflight 처리가 된다.
웹 브라우저는 Preflight에 대한 응답을 확인하여 자신이 권한이 있다면 메인 요청을 보내고, 그렇지 않으면 메인 요청을 보내지 않는다.
Preflight는 개발자의 설정 없이 조건에 맞게 브라우저가 전송, 수신하며, Preflight request / response 시 자동으로 추가되는 몇 가지 헤더가 있다.
HTTP 요청 헤더
-
Access-Control-Request-Method
메인 요청에서 클라이언트가 어떤 메서드 요청을 할 것인지를 알려준다.
ex) Access-Control-Request-Method : POST -
Access-Control-Request-Headers
메인 요청에서 클라이언트가 어떤 커스텀 헤더를 사용할지 알려준다.
ex) Access-Control-Request-Headers: CustomHeader
HTTP 응답 헤더
-
Access-Control-Allow-Method
이 서버가 어느 메서드 요청에 대해 응답 가능한지를 알려준다.
ex) Access-Control-Allow-Method : GET, POST
이는 해당 서버가 GET, POST 요청에 대해서만 응답 가능함을 의미한다. -
Access-Control-Allow-Header
이 서버가 어떤 커스텀 헤더를 인식할 수 있는지를 알려준다.
ex) Access-Control-Allow-Headers: CustomHeader
3. 인증정보를 포함한 요청
CORS에서 가장 특이한 점은 인증정보를 포함한 응답만을 받을 수 있도록 설정할 수 있다는 점이다. 기본적으로 XMLHttpRequest나 fetch 호출에서 브라우저는 자격 증명을 보내지 않기 때문에, 요청을 보낼 때 플래그를 설정해주어야 한다.
1 |
|
XMLHttpRequest.withCredentials 프로퍼티를 true로 설정하면 Cookie가 HTTP 요청 헤더에 포함되어 전송된다.
브라우저가 응답을 수용하기 위해선 서버는 아래 2가지 작업을 해야 한다.
- Access-Control-Allow-Credentials: true 헤더를 포함하여 HTTP 응답 메세지를 보내야 한다.
- Access-Control-Allow-Origin 헤더에 반드시 특정 값 (특정 Origin) 을 설정하여 응답해야한다. Simple requests나 Preflight requests에서는 허용되던 Access-Control-Allow-Origin: * 과 같이 응답하면 브라우저는 해당 응답을 무시한다.