CORS란?

CORS(Cross-Origin Resource Sharing)는 웹 브라우저 보안 정책 중 하나로,

같은 출처(Origin)가 아닌 리소스에 대한 웹 페이지의 접근을 제한하는 정책을 완화하기 위한 메커니즘이다.

웹 애플리케이션에서 다른 도메인의 리소스를 요청할 때 발생하는 보안 이슈를 관리하기 위해 사용된다.

출처(Origin)

 

URL 은 아래의 그림과 같은 요소로 구성 되어있다.

위의 구성요소 중에서 Protocol + Host + Port 3가지가 같으면 동일 출처(Origin)라고 한다.

// 동일 출처 예시
http://example.com:80       
http://example.com                 // http의 기본 port인 80이 생략되어있으므로 동일 출처

http://example.com/path1
http://example.com/path2       // protocol, host, port(생략)이 같으며, path부터 다르므로 동일 출처
-------------------------------------------------------------------------------------------------
//다른 출처 예시
http://example.com/path1
https://example.com/path2      // protocol 이 다르므로 다른 출처

http://www.naver.com
http://twogaether.site               // host 가 다르므로 다른 출처

http://example.com
http://example.com:8080         // port 가 다르므로 다른 출처

 


CORS 가 나온 이유

인터넷 기술이 여전히 생소했던 과거에는 크로스 사이트 요청 위조(CSRF) 문제가 발생했다.

CSRF 공격이란 인증된 사용자가 악의적인 웹사이트에서 의도하지 않은 요청을 송신하는 공격이다

아래는 은행 접속을 예시로 들어 CSRF 공격이 어떻게 작동하는지 간단히 설명한다.

 

  1. 사용자가 은행 계좌에 로그인한 상태에서 다른 탭에서 웹 사이트를 이용한다.
  2. 그러다가 악의적인 웹사이트에 방문한다.
  3. 이 웹사이트는 자동으로 특정 요청을 은행 웹사이트로 보내는 HTML 코드를 포함하고 있다.
    이 코드는 사용자가 은행 계좌에서 돈을 이체하는 등의 요청을 포함할 수 있다.
  4. 악의적인 웹사이트가 사용자의 브라우저에서 은행 웹사이트에 대한 요청을 보내기 때문에 브라우저는 사용자의 세션 쿠키를 함께 전송한다.
  5. 은행 웹사이트는 이 요청을 받았을 때, 브라우저의 세션 정보를 신뢰하고 사용자의 계좌에서 돈을 이체하는 요청으로 처리한다.

이 CSRF 문제를 방지하기 위해 이제 모든 브라우저에서 동일 출처 정책(Same-Origin-Policy)을 구현한다.

동일 출처 정책(Same-Origin Policy)

보통 스크립트로 로드되는 리소스에 대해 동일 출처 정책(Same-Origin Policy)를 적용한다.

동일 출처 정책은 보안상의 이유로 스크립트가 한 출처에서 로드한 문서나 스크립트에서 다른 출처의 리소스를 요청하는 것을 제한한다.

그러나 CORS는 이러한 제한을 우회하여 다른 출처의 리소스에 접근할 수 있도록 해준다.

 


Simple Request 와 Preflight

사실 나도 그랬고 다른 사람들도 그럴거라 생각하지만 이 개념에 대해 별 생각 없이 넘어가기 쉽다.

하지만 CORS 에 관해 제대로 이해하려면 백엔드, 프론트엔드 모두 이 과정을 이해해야 한다고 생각한다.

단순 요청 (Simple Request)

클라이언트 요청이 단순 요청의 조건에 해당하는 경우 예비 요청(Preflight) 없이 본 요청만으로 CORS 위반 여부를 검사한다.

단순 요청은 예비 요청없이 바로 서버에게 본 요청부터 보낸 후, 서버가 이에 대한 응답의 헤더에 

Access-Control-Allow-Origin과 같은 값을 보내주면 그때 브라우저가 CORS 정책 위반 여부를 검사하는 방식이다. 

단순 요청은 아래 조건들을 모두 만족해야 한다.

  1. 요청의 메소드는 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만 허용된다.

이 중 2, 3번 조건이 까다롭기 때문에 현실적으로 조건을 만족시키기 어렵다.

사용자 인증에 사용되는 Authorization 헤더도 사용하면 안되고, 무엇보다도 최근 웹 개발에서 주로 사용하며

대부분의 HTTP API에서 사용되는 application/json 컨텐츠 타입도 가지면 안된다. 

즉, POST 요청으로 바디에 데이터를 담아서 요청하려해도  application/json 헤더를 쓸 수 없다.

예비 요청(Preflight)

단순 요청의 조건을 만족하기 매우 까다롭기 때문에 일반적으로 웹 개발 시 가장 자주 마주치게 되는 시나리오이다. 

이 시나리오에서 브라우저는 요청을 예비 요청과 본 요청으로 나누어 서버에 전송한다. 

이때 브라우저가 본 요청을 보내기 전에 보내는 예비 요청을 Preflight라고 부른다. 

본 요청을 보내기 전에 브라우저 스스로 이 요청을 보내는 것이 안전한지 확인하는 과정으로, 

HTTP 메소드 중 OPTIONS 메소드가 사용된다. 

이 시나리오의 과정은 아래와 같다.

  1. 프론트엔드에서 자바스크립트의 fetch API를 이용해 브라우저에게 리소스를 받아오라는 명령을 내리면
  2. 브라우저는 서버에게 예비 요청을 먼저 보내고, 서버는 이 요청에 대한 응답으로
    현재 자신이 어떤 것들을 허용하고 금지하는지에 대한 정보를 응답 헤더에 담아 브라우저에게 다시 보내준다.
  3. 이후 브라우저는 자신이 보낸 예비 요청과 서버가 응답에 담아준 허용 정책을 비교한 후
    요청을 보내도 안전하다고 판단되면 같은 엔드포인트로 다시 본 요청을 보낸다.
  4. 이후 서버가 본 요청에 대한 응답을 하면 브라우저는 최종적으로 응답 데이터를 자바스크립트에게 넘겨준다.

 


CORS 동작 방식

  1. 요청 (Request) 전송
    - 브라우저에서 한 출처의 웹 페이지가 다른 출처의 리소스에 HTTP 요청을 보낸다.
  2. 프리플라이트 옵션 요청 (Preflight Request) 확인
    - 만약 요청이 특정 조건을 충족하는 경우 (Simple Request 가 아닌 경우),
    브라우저는 사전에 서버에게 요청 전에
    '프리플라이트'라고 불리는 OPTIONS 메서드로 요청을 보낸다. 
    - 이때 서버는 허용된 메서드(예: GET, POST) 및 헤더 등에 대한 정보를 응답한다.
  3. 실제 요청 (Actual Request)
    - 프리플라이트 요청이 성공하면, 실제로 리소스에 대한 실제 HTTP 요청(GET, POST 등)이 이루어진다.
  4. 응답 (Response)
    - 서버는 요청을 받아들이고, 브라우저에게 응답을 보낸다.
    - 응답에는 어떤 출처에서 리소스에 접근할 수 있는지를 나타내는 'Access-Control-Allow-Origin' 헤더 등이 포함된다.

만약 서버에서 특정 출처의 요청을 허용한다고 헤더에 명시되어 있지 않다면, 브라우저는 해당 요청을 차단한다.

또한 응답을 200으로 받아도 바디에 아무 값도 안담겨 오는 경우에도 CORS 문제를 생각해 볼 수 있다.

 

'네트워크' 카테고리의 다른 글

[네트워크] JWT 란  (2) 2023.10.04
x-www-form-urlencoded와 json  (0) 2023.09.21
[네트워크] HTTP 요청 데이터  (0) 2023.07.25
웹 서버(WS)와 WAS 및 분리 이유  (0) 2023.04.03
[네트워크] REST API 란?  (0) 2023.03.27

+ Recent posts