[WEB] CORS 개념 정리

심언조·2021년 6월 21일
0

WEB

목록 보기
2/2
post-thumbnail
post-custom-banner

📕왜 CORS를 공부해야 할까?

웹 개발자라면 CORS(Crose-Origin Resource Sharing) 정책위반으로 인해 에러가 발생하는 상황을 겪어 봤을 것이다. 이런 상황이 왜 일어나는지 모른다면 문제를 해결하는데 애를 많이 먹게 될 것이다.
이번에는 CORS가 무엇이고 어떻게 동작하는지 정리하려고 한다.

📙다른 출처간의 리소스 공유 : Cross-Origin Resource Sharing(CORS)

MDN 에서는 아래와 같이 설명하고 있다.

교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다. 웹 애플리케이션은 리소스가 자신의 출처(도메인, 프로토콜, 포트)와 다를 때 교차 출처 HTTP 요청을 실행합니다.

"교차 출처"는 "다른 출처"라고 표현하는게 좀 더 쉽게 이해할 수 있기 때문에 이 글에서는 "다른 출처"라는 단어를 사용할 것이다.

출처(Origin)란?

다른 출처간의 리소스 공유에 대해서 알아보기 전에 출처가 무엇을 의미하는지 이해할 필요가 있다. 서버의 위치를 의미하는 https://google.com 과 같은 URL은 하나의 문자열 같지만 다음과 같이 구성되어 있다.

이때 출처는 ProtolcolHost 그리고 Port까지 모두 합친 것을 의미한다.
브라우저의 개발자 도구 콘솔에서 Location 객체가 가지고 있는 origin 프로퍼티에 접근하여 현재 출처를 알아낼 수도 있다.

console.log(location.origin);
// "https://velog.io"

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

웹에서는 다른 출처의 리소스 요청을 제한하는 것과 관련된 두가지 정책이 존재한다. 한 가지는 이 포스트의 주제인 CORS 다른 한가지는 SOP(Same-Origin Policy)이다.

SOP는 "같은 출처에서만 리소스를 공유할 수 있다"라는 규칙을 가진 정책이다. 그러나 웹에서는 다른 출처에 있는 리소스를 가져와서 사용하는 일은 굉장히 흔한 일이라 무작정 막을수도 없기에 몇가지 예외조항을 두고 이 조항에 해당하는 리소스 요청은 리소스의 출처가 다르더라도 허용하기로 했는데, 그중 하나가 "CORS 정책을 지킨 리소스 요청"이다.

즉, 다른 출처의 리소스를 사용하는 것을 제한하는 행위는 하나의 정책만으로 결정된 사항이 아니라는 의미가 된다.

이런 정책들은 악의를 가진 사용자가 CSRF(Cross-Site Request Forgery), XSS(Cross-Site Scripting)과 같은 방법을 사용해 사용자의 정보를 탈취를 막는 보호막 역할을 해준다.

출처를 비교하는 방법

두개의 출처를 비교하는 방법은 URL의 구성요소 중 Protocol, Host, Port 이 세가지가 동일한지 확인하면 된다. 즉 같은 프로토콜, 호스트, 포트를 사용한다면 다른 요소는 다르더라도 같은 출처로 인정된다. 반대로 리소스가 자신의 출처와 다를경우 브라우저는 교차출처 요청을 실행한다.

출처를 비교하는 로직은 서버에 구현된 스펙이 아닌 브라우저에 구현된 스펙이다. 만약 CORS정책을 위반하는 요청에 서버가 정상적으로 응답을 하더라도 브라우저가 이 응답을 분석해서 CORS정책에 위반되면 그 응답은 처리하지 않게 된다.

CORS의 동작 방식

다른 출처의 리소스를 요청할 때 HTTP 프로토콜을 사용하여 요청을 보내게 되는데, 이때 브라우저는 요청헤더에 Origin이라는 필드에 요청을 보내는 출처를 함께 담아서 보낸다.

Origin: https://velog.io

이후 서버가 요청에 대한 응답을 할 때 응답 헤더의 Access-Countrol-Allow-Origin이라는 값에 접근이 허용된 출처를 알려주고 응답을 받은 브라우저는 클라이언트가 보낸 요청의 Origin과 서버가 보낸준 응답의 Access-Countrol-Allow-Origin을 비교하여 이 응답이 유효한 응답인지 아닌지 결정한다.

기본적인 흐름은 간단하지만 CORS가 동작하는 방식은 한가지가 아닌 세가지 시나리오에 따라 변경된다.

Simple Request

이 시나리오에 대한 정식명칭은 없지만 MDN에서 "단순요청"이라는 용어를 사용하므로 본 포스트에서도 단순요청이라고 부르겠다.

단순요청은 이 다음에 다룰예비 요청(Prefilght)을 보내지 않고 바로 서버에 본 요청을 한 후, 서버가 이에 대한 응답의 헤더에 Access-Control-Allow-Origin과 같은 같을 보내주면 브라우저가 CORS정책 위반여부를 검사하는 방식이다.

단순요청은 특정한 조건을 만족하는 경우에만 성립할 수 있다.

  1. 요청의 메소드는 GET, HEAD, POST 중 하나여야 한다.
  2. 유저 에이전트가 자동으로 설정한 헤더외에, 수동으로 설정할 수 있는 헤더는 Fetch 명세에서 "CORS-safelisted request-header"로 정의한 헤더만 사용할 수 있다.
    • 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

위 조건과 같이 다소 까다로운 조건들이 많기 때문에 위 조건을 모두 만족시키는 상황을 만드는것은 쉽지 않다.

Prefilght Request

프리플라이트(Prefilght) 시나리오에 해당하는 상황일 때 브라우저는 요청을 한번에 보내지 않고 예비 요청과 본 요청으로 나누어서 서버로 전송한다.

이때 브라우저가 예비요청을 보내는 것을 Preflight라고 부르며 예비요청은 OPTIONS메소드를 사용한다. 예비요청의 역할은 본 요청을 보내기전 브라우저가 요청을 보내는 것이 안전한지 확인하는 것이다.

자바스크립트의 fetch API를 통해 브라우저에게 리소스를 받아오게 하면 브라우저는 서버로 예비요청을 먼저 보내고 서버는 이 예비요청에 대한 응답으로 어떤 것을 허용하고 어떤것을 금지하고 있는지에 대한 정보를 담아서 브라우저로 다시 보내준다.

이후 브라우저는 보낸 요청과 서버가 응답해준 정책을 비교하여 해당 요청이 안전한지 확인하고 본 요청을 보내게 된다. 이후 서버가 본 요청에 대한 응답을 하면 최종적으로 이 응답 데이터를 자바스립트로 넘겨준다.

Credentialed Request

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

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

이 옵션은 총 3가지 값을 사용할 수 있다.

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

만약 same-origin 이나 include와 같은 옵션을 사용하여 리소스 요청에 인증 정보가 포함된다면, 브라우저는 다른 출처의 리소를 요청할 때 Access-Control-Allow-Origin만 확인하는 것이 아니라 다른 조건을 추가로 검사한다.

요청에 인증정보가 담겨있는 상태에서 다른 출처의 리소스를 요청하게 되면 브라우저는 CORS정책 위반 여부를 검사하는 룰에 다음 두가지를 추가하게 된다.

  1. Access-Control-Allow-Origin 에는 모든 요청을 허용하는*을 사용할 수 없으며, 명시적인 URL이어야 한다
  2. 응답 헤더에는 반드시 Access-Control-Allow-Credentials:true가 존재해야 한다

이렇게 CORS 정책에 대한 3가지 시나리오를 알아두면 실제 상황에서 정책 위반으로 인한 문제가 생겼을 때 시간을 단축시킬 수 있으니 숙지해놓는 것을 추천한다.

✏️정리

  • 프로토콜, 호스트, 포트번호가 같다면 같은 출처(Same-Origin)로 판단한다.
  • CORS 정책은 출처가 다른 리소스를 접근할 때 HTTP 헤더를 사용하여 권한을 부여하도록 브라우저에 알려주는 체제다.
  • 출처를 비교하는 스펙은 브라우저에 구현되어 있다.
  • CORS는 Simple Request, Prefilght Request, Credentialed Reqeust 3가지 동작방법이 있다.

📚참고링크

MDN - 교차 출처 리소스 공유(CORS)

profile
웹 개발자
post-custom-banner

0개의 댓글