CORS에 대해서

이정규·2021년 10월 18일
0

CORS

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이란?

Origin이란 출처를 의미한다. 출처는 URL에서 뜯어볼 수 있다.

https://www.naver.com:80/users?name="jeongGyu"#foo
이름
Protocolhttps://
Hostwww.naver.com
Port:80
Path/users
Query String?name="jeongGyu"
Fragment#foo

여기서 출처는 Protocol + Host + Port 를 뜻한다.

포트번호는 http, https에서는 정해져있기 때문에 생략가능하다. 하지만 포트번호가 명시적으로 포함되어 있다면 포트번호까지 일치해야 같은 출처로 인정이 된다.

⇒ 포트번호까지 일치여부는 각 브라우저마다 다르다. Internet Explorer는 포트번호를 무시한다.

CORS 정책 위반은 어디서 판단할까?

CORS 정책은 "브라우저"에서 판단한다. 서버에서는 판단하지 않는다.

CORS는 어떻게 동작할까?

  1. 웹 클라이언트 어플리케이션이 다른 출처의 리소스를 요청할 때는 HTTP 프로토콜을 사용하여 요청을 보내게 된다. 이 때 브라우저는 요청 HeaderOrigin 이라는 필드에 요청을 보내는 "출처"를 함께 담아보낸다.
  2. 서버에서는 해당 요청을 받고 응답 HeaderAccess-Control-Allow-Origin 이라는 필드에 "이 리소스에 접근 가능한 출처들"을 내려준다.
  3. 응답을 받은 브라우저는 자신이 보냈던 요청 HeaderOrigin필드 값과 응답 HeaderAccess-Control-Allow-Origin의 값을 비교하여 유효한지 판단한다.

CORS 정책 위반 확인의 시나리오들

1. Preflight Request

  • 동작 과정
    1. 브라우저가 서버에게 "본 요청"을 보내기 전에 "예비 요청"을 보낸다. 이를 preflight라고 부른다.
      이 예비 요청에는 HTTP 메소드중 OPTIONS 메소드가 사용된다.
    2. 서버에서 Access-Control-Allow-Origin 을 응답한다.
    3. 비교한 뒤, 가능하다면 본 요청을 진행한다. 아니라면 CORS 정책 위반으로 끝을 낸다.

2. Simple Request

  • 동작 과정

    1. 브라우저는 서버에게 "본 요청"에 해당하는 걸 바로 보낸다.

    2. 서버에서 그에 대한 리소스와 Access-Control-Allow-Origin 을 응답한다.

    3. 비교한 뒤, 가능하다면 본 요청을 진행한다. 아니라면 CORS 정책 위반으로 끝을 낸다.

      특수한 조건을 만족해야만 위 과정이 가능하다. 예비 요청을 생락할 수 있는 조건들은 다음과 같다.

    • Request Method는 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만 허용된다.

3. Credentialed Request

  • 동작 과정
    Preflight Request과정과 같다.

  • 추가된 부분

    Credentials라는 속성을 같이 보낸다. 보낼 때, Access-Control-Allow-Origin: * 을 서버에서 설정해주면 안되고, 응답 헤더에는 Access-Control-Allow-Credentials: true 가 존재해야한다.

    Credentials 에는 3가지 옵션이 존재한다.

    옵션명설명
    same-origin같은 출처 간 요청에만 인증 정보를 담을 수 있다.
    include모든 요청에 인증 정보를 담을 수 있다.
    omit모든 요청에 인증 정보를 담지 않는다.

    Credentials: 'include'이 값을 설정한 뒤 브라우저가 서버에게 요청을 한다면, 브라우저는 인증 정보들이 담겨져 있는 "쿠키 정보"를 함께 담아서 보낸다.

    그러면 서버에서 허용된 인증정보를 브라우저에게 보내고, 브라우저는 이를 판단하고 통신하게 된다.

출처
https://evan-moon.github.io/2020/05/21/about-cors/

profile
강한 백엔드 개발자가 되기 위한 여정

0개의 댓글