Cross Origin Resource Sharing의 약자로 교차 출처 리소스 공유라는 약자이다.
원래는 클라이언트가 서버로 요청을 보낼때 동일한 출처를 가져야만 리소스를 공유할 수 있다.
www.v1.jeong.com
라는 클라이언트가 존재하고
www.v1.jeong-a.com
라는 서버가 존재한다고 해보자.
이는 서로 출처가 다르다. 이 때 서버가 CORS설정을 하지 않고 클라이언트가 서버에게 요청을 보낸다면?
서버는 자신이 허용하고 있는 출처를 Access-Allow-Control-Origin
이라는 헤더로 보내준다.
브라우저는 해당 헤더를 보고 나의 출처가 여기에 포함되는지 확인한다. 포함되지 않았다면 CORS에러를 내뱉고 더이상 통신을 진행하지 않는다.
서버는 브라우저에게 받은 요청을 받고 응답으로 자신이 허용한 Origin-Header를 보내게 된다.
그러면 해당 Origin-Header를 보고 브라우저가 해당 서버가 허용하지 않았다면 에러를 브라우저에서 내뱉고 허용했다면 해당 서버와는 통신이 가능한 것이므로 통신을 계속한다.
CORS정책은 서버를 보호하기 위한 정책이다.
CORS에 대해서 다시 공부해보니깐 이는 서버를 보호하기 위한 목적이기 보다는 브라우저를 사용하는 사용자를 보호하기 위한 목적이라고 볼 수 있다.
자, 기본적으로 default값은 동일 출처인 경우에만 가능하다고 되어있다.
이렇게 되면 누군가가 클라이언트를 모방하여 똑같은 사이트를 만들고 이에 대한 API요청은 원본 서버를 걸어놓고 또, 그들이 만든 새로운 서버로 보내게 탈취한다고 생각해보자.
그러면 사용자는 잘 동작하는 사이트라고 생각할 수 있다. 하지만 이는 그들이 만든 새로운 서버에 사용자의 데이터도 함께 빠져나가므로 보안적인 문제가 생긴다.
이렇게 누군가가 클라이언트를 모방하고 함부로 api요청을 할 수 있게 되게하면 브라우저를 사용하는 사용자는 위험하게 된다.
하지만, 직접 개발해보면 서버 따로 클라이언트 따로 개발하는 경우가 많아진다.
이 때, 서버에 CORS를 설정하여 허용하는 클라이언트만을 적용하여 사용자를 보호할 수 있게 만든다.
현재 출처 확인 방법
location.origin
하면 현재 출처를 확인할 수 있다.
Origin이란 출처를 의미한다. 출처는 URL에서 뜯어볼 수 있다.
https://www.naver.com:80/users?name="jeongGyu"#foo
이름 | 값 |
---|---|
Protocol | https:// |
Host | www.naver.com |
Port | :80 |
Path | /users |
Query String | ?name="jeongGyu" |
Fragment | #foo |
여기서 출처는 Protocol + Host + Port
를 뜻한다.
포트번호는 http, https에서는 정해져있기 때문에 생략가능하다. 하지만 포트번호가 명시적으로 포함되어 있다면 포트번호까지 일치해야 같은 출처로 인정이 된다.
⇒ 포트번호까지 일치여부는 각 브라우저마다 다르다. Internet Explorer는 포트번호를 무시한다.
CORS 정책은 "브라우저"에서 판단한다. 서버에서는 판단하지 않는다.
요청 Header
에 Origin
이라는 필드에 요청을 보내는 "출처"를 함께 담아보낸다.응답 Header
에 Access-Control-Allow-Origin
이라는 필드에 "이 리소스에 접근 가능한 출처들"을 내려준다.요청 Header
의 Origin
필드 값과 응답 Header
의 Access-Control-Allow-Origin
의 값을 비교하여 유효한지 판단한다.OPTIONS
메소드가 사용된다.Access-Control-Allow-Origin
을 응답한다.동작 과정
브라우저는 서버에게 "본 요청"에 해당하는 걸 바로 보낸다.
서버에서 그에 대한 리소스와 Access-Control-Allow-Origin
을 응답한다.
비교한 뒤, 가능하다면 본 요청을 진행한다. 아니라면 CORS 정책 위반으로 끝을 낸다.
특수한 조건을 만족해야만 위 과정이 가능하다. 예비 요청을 생락할 수 있는 조건들은 다음과 같다.
GET, HEAD, POST
중 하나여야 한다.Accept
, Accept-Language
, Content-Language
, Content-Type
, DPR
, Downlink
, Save-Data
,ViewPort-Width
, Width
를 제외한 헤더를 사용하면 안된다.Content-Type
을 사용하는 경우에는 application/x-www-form-urlencoded
, multipart/form-data
, text/plain
만 허용된다.동작 과정
Preflight Request과정과 같다.
추가된 부분
Credentials
라는 속성을 같이 보낸다. 보낼 때, Access-Control-Allow-Origin: *
을 서버에서 설정해주면 안되고, 응답 헤더에는 Access-Control-Allow-Credentials: true
가 존재해야한다.
Credentials
에는 3가지 옵션이 존재한다.
옵션명 | 설명 |
---|---|
same-origin | 같은 출처 간 요청에만 인증 정보를 담을 수 있다. |
include | 모든 요청에 인증 정보를 담을 수 있다. |
omit | 모든 요청에 인증 정보를 담지 않는다. |
Credentials: 'include'
이 값을 설정한 뒤 브라우저가 서버에게 요청을 한다면, 브라우저는 인증 정보들이 담겨져 있는 "쿠키 정보"를 함께 담아서 보낸다.
그러면 서버에서 허용된 인증정보를 브라우저에게 보내고, 브라우저는 이를 판단하고 통신하게 된다.