먼저 출처의 개념을 이해하고 가야하는데 여기서 말하는 출처란
프로토콜+도메인+포트
를 합친 부분이다.
내 벨로드의 url이 https://velog.io/@now-0o로 보자면
https://
(포트), velog.io
(도메인), :80
(웹 기본포트)가 같아야 동일 출처가 된다.
SOP(Same Origin Policy)동일 출처 정책과 CORS(Cross Origin Resource Sharing)은 긴밀한 관계를 가지고 있다.
먼저 SOP:동일 출처 정책
은 다른 Origin으로 요청을 보낼 수 없게하는 브라우저의 기본 정책이다. 때문에 다른 Origin으로 요청을 보내는 것은 불가능 했지만 세월이 지나고 다른 Origin으로 요청을 해야할 상황이 많아지자 예외의 상황을 두고 다른 Origin으로 요청을 보낼 수 있게 되었다.
<script>
태그로 자바스크립트를 실행하는 경우.<link>
태그로 스타일시트를 불러오는 경우.이렇게 CORS는 우리의 요청을 막는 것이 아닌 동일 출처 정책에서 예외로 요청을 풀어주는 정책이였다.
CORS는 브라우저 정책이기 때문에 서버에서 응답을 하지 않는 것이 아니다.
서버는 정상적으로 응답을 보내지만 브라우저에서 안전한지 판단하고 위험하다가 판단되면 요청은 버려지게 된다.
동일 출처 정책으로 허용되지 않은 요청을 막지 않는다면?
만약 해커가 만든 사이트에 접속할 경우 사용자 개인정보를 빼내는 요청이 보내질 것이다. 사용자는 웹 사이트에 접속 한번 했을 뿐인데 브라우저에 저장되어 있던 사용자의 정보는 이미 해커에게 들어갔을 것이다.
그렇다면 CORS는 어떻게 동작하는 것일까?
결국 CORS 해결은 서버가 응답을 할 때 Access-Control-Allow-Origin 헤더에 Origin을 설정하거나 *(전체)를 설정하면 된다.
CORS에 대해 더 깊게 들어가면 CORS 정책은 3가지 방식으로 나누어서 설명해야한다.
단순 요청은 위의 CORS의 동작원리와 동일하게 예비 요청없이 서버에 바로 요청을 보내는 방법이다. 서버로부터 응답이 오면 브라우저에서 CORS 위반 여부를 확인한다.
GET
,HEAD
,POST
만 가능하다.Accept
, Accept-Language
, Content-Language
, Content-type
, DPR
, Downlink
, Save-Data
, Viewport-Width
, Width
헤더만 사용 가능하다.application/x-www-form-urlencoded
, multipart/form-data
, text/plain
중 하나여야만 한다.보통의 요청은 application/json
이나 text/xml
로 통신하기 때문에 대부분의 API 요청은 예비 요청으로 이루어진다.
예비 요청은 바로 서버에 요청을 보내지 않고 요청이 유효한지 예비 요청을 보낸 뒤 본 요청을 보내는 방법이다. 이러한 예비 요청을 Preflight라고 하며 HTTP Method는 OPTIONS를 사용한다.
먼저 브라우저에서 예비요청을 보낼 때
1. Origin에 출처를 담는다.
2. Access-Control-Request-Method에 실제 요청에 사용할 메소드를 담는다.
3. Access-Control-Request-Headers에 실제 요청에 사용할 헤더를 담는다.
이렇게 요청을 받은 서버는
1. Access-Control-Allow-Origin에 허용할 출처를 설정한다.
2. Access-Control-Allow-Methods에 허용할 메소드를 설정한다.
3. Access-Control-Allow-Headers에 허용할 헤더들을 설정한다.
4. Access-Control-Allow-Age에 이 예비요청이 브라우저에 캐시될 수 있는 초단위를 설정한다.
마지막으로 브라우저는 보낸 예비요청과 받은 응답을 비교하여 안전한지 확인 후 본 요청을 보낸다.
인증 요청은 클라이언트에서 서버에게 자격 인증 정보(Credential)를 담아서 보낼 때 사용한다. 이때 자격 인증 정보는 session id가 저장된 cookie나 Authorization 헤더에 담은 토큰을 의미한다.
보통 브라우저에서 제공하는 요청 API는 cookie나 인증 데이터를 담을 수 없다. 그래서 이런 인증 데이터를 담을 때에는 credentials이라는 옵션을 사용해야 한다. 이 credentials는 3가지 값을 사용할 수 있다.
값 | 설명 |
---|---|
same-origin(기본값) | 같은 출처 간의 요청일때만 인증 정보만 담을 수 있다. |
include | 모든 요청에 인증 정보를 담을 수 있다. |
omit | 모든 요청에 인증 정보를 담을 수 없다. |
이런 정보를 설정하지 않으면 쿠키나 인증정보는 서버로 보내지지 않는다.
서버에서도 인증 요청은 다르게 대응해야 하는데
1. Access-Control-Allow-Credentials에 true를 설정해야 한다.
2. Access-Control-Allow-Origin에 *값을 사용할 수 없다.
3. Access-Control-Allow-Methods에 *값을 사용할 수 없다.
4. Access-Control-Allow-Headers에 *값을 사용할 수 없다.
즉 Access-Control-Allow-Credentials를 true로 설정해준 뒤
응답 Access-Control-Allow-Origin 헤더에 분명한 값이 설정되어야 한다는 것이다.