CORS

yimo22·2023년 4월 13일
0

WEB

목록 보기
5/8

CORS

웹을 개발하다보면 CORS 정책을 위반했다고 에러가 발생하는 경우가 무척 많다. 웹 개발을 하면서 겪게 되는 CORS에 대해서 더이상 시달리기 싫어 본 글을 작성한다.

CORS는 (CORS : Cross Origin Resource Sharing) 다른 출처 리소스 공유

CORS의 발생 원인

CORS 가 일어나는 이유는, 웹 브라우저에서 HTTP 요청을 하는 과정에서 각각 어떤 요청을 하느냐에 있기 때문이다. 웹 클라이언트 어플리케이션이 다른 출처의 리소스를 요청할 때는 HTTP 프로토콜을 사용하여 요청을 보내게 된다. 이때 '이 리소스에 접근이 주소지' 를 헤더에 담아 서버와 통신을 하게 되는데, 이 헤더에 들어있는 정보와 정보가 불일치하여 에러가 발생하게 된다.

POLICY

먼저 웹 생태계에서 리소스 요청을 제한하는 것과 관련한 두가지 정책을 알고가면 더 이해가 잘 될것 같다. 웹에는 크게 2가지 정책을 갖고 있다.

  • Same Origin 정책 (SOP)
    'SOP는 같은 출처에서만 리소스를 공유할 수 있다.' 라는 규칙을 가진 정책이다. 그러나 웹이라는 Open된 환경에서 다른 출처에 있는 리소스를 가져와서 사용하는 일은 매우 흔한 일이라 이를 무작정 막을 수 없다. 따라서 몇 가지 예외 조항을 두고 이 조항에 해당하는 리소스의 요청은 출처가 다르더라도 허용하기로 한다.

즉, 동일 출처 서버에 있는 자원들은 자유롭게 가져올 수 있지만, 다른출처 서버에 있는 자원들은 가져올 수 없다는 것을 뜻한다.

  • Cross Origin 정책 (COP)
    SOP의 예외조항으로 출처가 다른 몇가지 주소들을 예외로 두어 이를 허용하는 정책이다.

우리가 다른 출처로 리소스를 요청한다면, SOP 정책을 위반한 것이 되고 SOP의 예외조항인 CORS 정책까지 지키지 않는다면 다른 출처의 리소스를 사용할 수 없게 되는 것이다.

즉, 다른 출처의 리소스를 사용하는 것을 제한하는 행위는 하나만의 정책(ex. SOP)만으로 결정된 사항이 아니다. SOP 에서 정의된 예외 조항과 CORS를 사용할 수 있는 케이스들이 맞물리지 않는 특수한 케이스에만 리소스 요청이 불가능하게 막는 것이다.

위의 정책을 사용하는 이유는 간단하다. 보안성 때문이다. 웹에서 작동하는 Client Application 들은 사용자의 공격에 매우 취약하다. 예를들어 지금 당장 F12 를 눌러 개발자모드를 통해서 해당 HTML의 구조와 리소스의 출처들을 크게 공들이지 않고(?) 열람할 수 있다.

그렇기 때문에 악의를 가진 사용자가 소스코드를 구경한 후 CSRF나 XSS와 같은 방법을 사용해서 어플리케이션에서 코드가 실행된 것처럼 꾸며서 사용자의 정보를 탈취하기가 쉬워져 보안성이 취약해지기 때문이다.

💡 CSRF
사이트 간 요청 위조(또는 크로스 사이트 요청 위조, 영어: Cross-site request forgery, CSRF, XSRF)는 웹사이트 취약점 공격의 하나로, 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위(수정, 삭제, 등록 등)를 특정 웹사이트에 요청하게 하는 공격을 말한다.

유명 경매 사이트인 옥션에서 발생한 개인정보 유출 사건에서 사용된 공격 방식 중 하나다.

사이트 간 스크립팅(XSS)을 이용한 공격이 사용자가 특정 웹사이트를 신용하는 점을 노린 것이라면, 사이트간 요청 위조는 특정 웹사이트가 사용자의 웹 브라우저를 신용하는 상태를 노린 것이다. 일단 사용자가 웹사이트에 로그인한 상태에서 사이트간 요청 위조 공격 코드가 삽입된 페이지를 열면, 공격 대상이 되는 웹사이트는 위조된 공격 명령이 믿을 수 있는 사용자로부터 발송된 것으로 판단하게 되어 공격에 노출된다.

출처 : WIKI 백과 [CSRF]

Origin

우리가 인터넷 브라우저에서 어떤 사이트를 접속할 때 URL을 통해서 접근하게 된다. 가령 네이버를 접속한다고 가정해보자. 그렇다면 다음과 같은 URL을 통해서 접속할 것이다.

1. https://www.naver.com/[querystrings]
2. http://www.naver.com/[querystrings]

웹 브라우저는 사용자가 입력한 URL을 토큰화 하여 연속된 작업을 실행한다. 이때 자원의 출처는 3가지 파트로 나뉜다.

  1. Protocol
    URL의 처음부분에 등장하는 것으로 어떤 프로토콜을 사용하여 통신을 할지 정하는 부분이다.
    위의 예시1 에서는 Https를 사용하며, 예시2 에서는 Http 프로토콜을 사용한다.
  2. Host
    해당 사이트의 도메인을 뜻한다. www.naver.com 이 도메인이 되며, 사실은 IP 주소(숫자)로 이루어져있다. 하지만 DNS 에 의하여 인간이 읽을 수 있는 문자형태로 표현할 수 있게 되었다.
  3. Port
    예시1과 예시2 에서는 포트번호를 명시해주지 않았다. 하지만 Http 프로토콜에서는 포트번호를 명시하지 않을 경우 80, Https에서는 443 포트를 default로 사용한다.
    따라서 위의 1번은 [:80], 2번은 [:443] 이 생략된 것이다.

CORS에서 Protocol + Host + PORT 를 자원의 출처(Origin)으로 본다.

IE

Origin 에 대한 판단은 웹 브라우저 마다 상이하다. 가장 대표적인 것이 IE이다. IE는 출처를 비교할 때 Port 부분은 무시한다. 이로 인해 보안에 취약해지게 되었다. (이외에 표준을 따르지 않아서 따로 코드를 넣어 개발을 해야한다는...)

detail

결국 이런 출처의 구분은 WEB브라우저 가 진행한다. 이를 다시말해보면, 서버 간에 통신을 할 때는 정책이 적용되지 않는다는 말도 된다.

즉, 클라이언트단에서 API를 요청하는게 아니라, 서버단에서 다른 출처의 서버로 API를 요청하면 CORS 에러로부터 자유로워 진다. 그래서 이를 이용한 Proxy 서버라는 것이 있다.

앞에서 말했듯이, SOP 정책을 위반해도 CORS 정책에 따르면 다른 출처의 리소스도 허용하여 사용할 수 있다. 그렇다면 CORS의 브라우저 작동과정과 방법들을 살펴보자

Preflight Request

일반적인 웹 어플리케이션을 개발할 때 가장 자주 마주치는 시나리오 이다. 이 방법 핵심은 예비요청 과 본요청으로 나누어 서버로 전송한다는 것에 있다.

예비 요청에서 Http의 메소드 중, Option 메소드가 사용되어 본 요청을 보내기 전 브라우저 스스로 이 요청이 안전하지 확인하는 과정을 거친다.

Preflight Request

예비요청에서 브라우저는 Access-Control-Request-Header 를 사용하여 자신이 본 요청에서 Content-Type 헤더를 사용할 것을 알려주거나, Access-Control-Request-Method를 사용하여 이후 사용할 메소드를 서버에게 미리 알려준다.

예비요청을 통해서 서버로부터 받은 결과를 통해서, 브라우저는 리소스의 요청을 체크하여 CORS 위반사항을 판단한다.

Simple Request

이 방식은 예비요청없이 본 요청만으로 CORS 정책 위반여부를 검사한다.
즉, 바로 서버가 본 요청의 응답으로 'Access-Control-Allow-Origin' 의 값을 보내주면 그때 브라우저가 CORS 위반여부를 판단하여 검사하는 방식이다.

하지만 이 방식은 아무때나 단순 요청을 사용할 수 있는 것이 아니고, 특정 조건을 만족하는 경우에만 예비요청을 생략할 수 있다. (이 조건을 만족시키기는 다소 까다롭다.)

  1. 요청의 METHOD 는 GET, HEAD, POST 중 하나여야 한다.
  2. Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width를 제외한 헤더를 사용하면 안된다.
  3. 만약 Content-Type을 사용하는 경우에는 application/x-www-form-urlencoded, multipart/form-data, text/plain 만 허용된다.

헤더에 Authorization나, text/html과 application/json 의 컨텐츠 타입을 가지도록 설계되는 시스템이 많기 때문에 거의 사용되지 않는다....

Credentialed Request

이는 인증된 요청을 사용하는 방법이다. 이 방식은 CORS의 기본적인 방식이라기 보다는 다른 출처 간의 통신에서 좀 더 보안을 강화하고 싶을 때 사용하는 방법이다.

기본적으로 브라우저가 제공하는 비동기 리소스 요청 API인 XMLHttpRequest 객체나 fetch API는 별도의 옵션 없이 브라우저의 쿠키 정보나 인증과 관련된 헤더를 함부로 요청에 담지 않는다. 이때 요청에 인증과 관련된 정보를 담을 수 있게 해주는 옵션이 바로 'credentials' 옵션이다.

이 옵션에는 총 3가지의 값을 사용한다.

  1. Same-Origin (default)
    같은 출처 간 요청에만 인증 정보를 담을 수 있다.
  2. include
    모든 요청에 인증 정보를 담을 수 있다.
  3. omit
    모든 요청에 인증 정보를 담지 않는다.

CORS 해결 방법

두서가 길었다. CORS 정책 위반 문제를 해결하는 가장 대표적인 방법은 기본적으로 서버에서 Access-Control-Origin 헤더에 알맞은 값을 세팅해 주는 것이다.

이 헤더는 Nginx나 Apache 같은 서버 엔진의 설정에서 추가할 수도 있고, 소스 코드내에서 응답 미들웨어 등을 사용하여 세팅할 수도 있다.

Webpack Dev Server

CORS를 가장 많이 마주치는 환경은 로컬에서 FE 어플리케이션을 개발하는 경우일 것이다.

BE에는 이미 'ACAO(Access-Control-Allow-Origin)' 헤더가 세팅되어 있겠지만, 이 중요한 헤더에다가 'https://localhost:3000' 과 같은 흔한 출처를 넣어주는 경우는 없다.

FE개발자는 대부분 Webpack과 webpack-dev-server를 이용하여 자신의 환경에 개발환경을 구축하는데, 이 라이브러리가 제공하는 프록시를 사용하여 CORS 정책을 우회할 수 있게 된다.

Webpack

이는 /api 로 시작하는 URL로 보내는 요청에 Proxy를 사용하여 {target} 으로의 프록싱을 해준다. 따라서 CORS 정책을 지킨 것처럼 브라우저를 속이면서(?) 서버와 통신이 가능해진다.

REF.

위키백과 : CSRF
https://ko.wikipedia.org/wiki/%EC%82%AC%EC%9D%B4%ED%8A%B8_%EA%B0%84_%EC%9A%94%EC%B2%AD_%EC%9C%84%EC%A1%B0

https://ko.wikipedia.org/wiki/%EB%8F%99%EC%9D%BC-%EC%B6%9C%EC%B2%98_%EC%A0%95%EC%B1%85

https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F#%EC%9A%94%EC%B2%AD_%EB%B0%A9%EC%8B%9D%EC%97%90_%EB%94%B0%EB%9D%BC_%EB%8B%A4%EB%A5%B8_cors_%EB%B0%9C%EC%83%9D_%EC%97%AC%EB%B6%80

https://baegofda.tistory.com/225

profile
Viva La Vida!

0개의 댓글