How to used CORS?
Published: 2024-06-23 15:45:28


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1fupn9f8rmf5jcdtlp5x.png)

## CORS?

웹 애플리케이션에서 HTTP 프로토콜을 통해 데이터를 요청하고 응답을 받는다.
즉, 웹 사이트는 URL이라는 출처를 가지고 요청을 하게 되는데, `동일 출처`에서 불러온 문서나 스크립트가 다른 출처에서 가져온 리소스와 상호작용 하는 것을 제한하는 보안 방식을 **SOP(Same Origin Polict)**라 한다.

이는 악의를 가진 사용자가 정보를 탈취하거나 잠제적인 악성 문서를 격리하기 위해 브라우저에서 사용되는 방식이다.

그러나 웹 이라는 오픈스페이스 환경에서 다른 출처에 있는 리소스를 사용하는 일이 빈번하게 발생하게 되고 무작정 막을 수 없기 때문에 **CORS(Cross Origin Resource Sharing)** 라는 보안 방식이 등장하게 되었다.

> `동일 출처`란 두 URL의 프로토콜, 포트(명시한 경우), 호스트가 모두 같은 출처이다.

### SOP(Same Origin Policy)

- 다른 출처의 리소스를 사용하는 것을 제한하는 보안 방식.
- `XMLHttpRequest`, `Fetch API` 처럼 JavaScript로 서로 다른 도메인에 대한 요청은 SOP 정책을 따른다.

### CORS(Cross Origin Resource Sharing)

- 다른 출처의 자원을 공유하는 보안 방식.
- 교차 출처 리소스 공유(CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다. 즉, 우리가 가져오는 리소스들이 안전한지 검사하는 관문이다.
- `link`, `script`, `img`, `video`, `audio`, `iframe` 태그 및 `@font-face` 처럼 교차 삽입의 경우 CORS 정책을 따른다.

> 출처를 비교하는 로직은 서버에 구현된 스펙이 아닌 `브라우저`에 구현된 스펙이다.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xkl7axxsjj32q7l49i7v.png)

정리하자면 CORS 정책이 적용되는 네트워크 요청의 경우, 네트워크 요청(request)에는 현재 요청을 보내는 사이트의 `Origin` 헤더가 존재해야 하고, 네트워크 응답(response)에는 그에 상응하는 `Access-Control-Allow-Origin` 헤더가 존재해야 한다.

결과적으로 요청의 `Origin` 헤더와 응답의 `Access-Control-Allow-Origin` 헤더에 명시된 도메인이 일치해야만 네트워크 요청이 정상적으로 마무리되고 우리가 원했던 리소스를 가져올 수 있게 된다. -> **CORS 이슈는 SOP에 의해 발생한다.**

항상 이론을 이해하면서 고개를 끄덕이지만 실제로 접하면...

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3vbublxmy3s0xvqdif6r.gif)

&nbsp;

## CORS 동작에 따른 3가지 시나리오

위에서 설명한 바와 같이 CORS는 다음과 같은 과정을 가진다.

1. 클라이언트에서 HTTP 프로토콜을 사용하여 요청을 보내게 되는데 이 때 헤더에 `Origin` 으로 요청의 출처를 담아서 함께 보낸다.

2. 서버에서 `Access-Control-Allow-Origin` 에 요청의 출처를 담아 클라이언트로 응답을 보낸다.

3. 클라이언트에서 응답을 받으면 브라우저가 `Origin`과 `Access-Control-Allow-Origin`을 비교하여 SOP를 위반했는지 확인한다.

이러한 동작은 아래의 3가지 시나리오에 의해 다시 달라진다.

- Preflight Request
- Simple Request
- Credentialed Request

### Preflight Request

브라우저가 예비요청과 본 요청으로 나누어 서버로 전송하게 되는데, 이 예비요청을 Preflight 라고 한다.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f38cjsyjf1638l6pde4g.png)

- 실제 요청이 전송하기 안전한지 확인하기 위해 실제 요청 이전에 수행하는 사전 요청이다.
- 서버는 응답 헤더에 `Access-Control-Allow-Origin` 값을 전달하여 브라우저가 SOP 위반 여부를 판단하게 한다.
- `Access-Control-Allow-Origin: * | (origin)`
- OPTIONS 메서드를 사용하여 사전 요청을 보낸다.
- **일반적으로 CORS가 발생하는 요청이다.**

### Simple Request

명확한 명칭은 없지만 Preflight Request를 보내지 않고 서버에게 요청을 하는 것을 단순 요청(Simple Request)라고 한다.

하지만 아무 때나 단순 요청을 사용할 수 있는 것은 아니고, 특정 조건을 만족하는 경우에만 예비 요청을 생략할 수 있다. 게다가 이 조건이 조금 까다롭기 때문에 일반적인 방법으로 웹 어플리케이션 아키텍처를 설계하게 되면 거의 충족시키기 어려운 조건들이라 필자도 이런 경우를 거의 경험하지는 못 했다.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6glu3fyv220fqof25luv.png)

- 사전 요청을 보내지 않고 바로 서버에 요청을 보낸다.
- 서버는 응답 헤더에 `Access-Control-Allow-Origin` 값을 전달하여 브라우저가 SOP 위반 여부를 판단하게 한다.
- `Access-Control-Allow-Origin: * | (origin) | null`
- `Access-Control-Allow-Origin: <origin>` 인 경우 Vary 속성도 같이 제공해야 한다. [MDN 참고](https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Vary)

> Simple Request는 아래 조건을 전부 만족해야 요청이 가능하다.
> - GET, HEAD, POST 메소드 중 하나를 사용
> - Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width 를 제외한 헤더를 사용하면 안된다.
> - Content-Type 사용 시 application/x-www-from-urlencoded, multipart/form-data, text/plain 만 허용

### Credentialed Request

Credentialed Request은 요청 시 보안을 강화하고 싶을 때 사용하는 방법이다.
일반적으로 XMLHttpRequest, fetch는 별도 옵션 없이 브라우저의 쿠키 정보, 인증과 관련된 헤더를 요청에 넣지 않지만, Credentialed Request를 통해 헤더에 인증과 관련된 정보를 넣을 수 있다.

이때 요청에 인증과 관련된 정보를 담을 수 있게 해주는 옵션이 바로 `credentials` 옵션이다.

> credentials 옵션 3가지
> - same-origin : 기본값이며, 같은 출처 간 요청에만 인증 정보를 담게 한다.
> - include : 모든 요청에 인증 정보를 담는다.
> - omit : 모든 요청에 인증 정보를 담지 않는다.

`same-origin`이나 `include`와 같은 옵션을 사용하여 리소스 요청에 인증 정보가 포함된다면, 이제 브라우저는 다른 출처의 리소스를 요청할 때 단순히 `Access-Control-Allow-Origin`만 확인하는 것이 아니라 `Access-Control-Allow-Credentials: true` 를 서버에서 응답 헤더에 포함하여 전달해 주어야 한다.

- 기존 CORS 방식에서 인증 관련 헤더를 포함할 때 사용하는 요청이다.
- 요청 헤더에 `credentials` 옵션을 사용하게 되면 인증 관련된 헤더를 넣을 수 있다.
- 서버는 응답 헤더에 `Access-Control-Allow-Origin : *` 값을 사용할 수 없으며, 명시적인 URL 이어야 한다.
- 서버는 응답 헤더에 반드시 `Access-Control-Allow-Credentials : true` 가 있어야 클라이언트의 인증 포함 요청에 허용이 가능하다.

---

ref: https://evan-moon.github.io/2020/05/21/about-cors/
ref: https://beomy.github.io/tech/browser/cors/