본문 바로가기
공부방

CORS는 왜 필요할까?

by hseong 2024. 3. 17.

CORS란

교차 출처 리소스 공유(Cross Site Resource Sharing, CORS)는 애플리케이션을 통합하기 위해 서로 다른 출처 간 요청을 허용하기 위한 메커니즘입니다. 기본적으로 모든 브라우저는 보안 상의 이유로 동일 출처 정책(SOP)을 구현하고 있으며 이로 인해 교차 출처 리소스 공유라는 별도의 메커니즘을 이용하여야 합니다.

CORS는 웹 어플리케이션 개발자라면 기본적으로 숙지해야 하는 내용입니다. 왜 동일 출처의 요청만을 허용하게 되었는지, 다른 출처의 요청을 허용하기 위한 CORS의 기본 메커니즘이 어떻게 되는지 알아야합니다.

왜 다른 출처의 요청을 허용하지 않는가?

과거에는 프론트엔드, 백엔드의 구분 없이 요청을 처리하고 브라우저를 렌더링하였습니다. 시간이 흐르면서 프론트엔드와 백엔드가 나뉘고 클라이언트가 서버의 API를 호출하는게 당연해졌습니다. 그리고 서로 다른 출처 간 요청 응답이 발생하면서 문제가 발생하기 시작했습니다.

이를 이해하기 위해서는 XSS, CSRF, SOP에 대해서 알 필요가 있습니다.

사이트 간 스크립팅(Corss Site Scripting, XSS)

사이트 간 스크립팅(Corss Site Scripting, XSS)는 웹 어플리케이션에서 사용자 입력값에 대한 필터링이 제대로 이루어지지 않을 경우 공격자가 악의적인 스크립트를 삽입하여 해당 스크립트가 희생자 측에서 동작하도록 하는 공격입니다. 이를 이용해서 공격자는 개인정보, 쿠키 탈취, 웹 페이지 변조 등의 공격을 수행할 수 있습니다.

사이트 간 요청 위조(Cross Site Request Forgery, CSRF)

사이트 간 요청 위조(Cross Site Request Forgery, CSRF)는 악의적인 공격자가 XSS와 같은 방법을 이용하여 사용자의 권한을 탈취하고 해당 권한으로 서버에 요청을 보내는 공격입니다. 서버에서 사용자의 권한을 확인하는 고정된 값(예. 쿠키)이 존재한다면 이를 탈취해서 개인정보 수정과 같은 임의의 행동을 실행하도록 만드는 것입니다.

예를 들어 공격자가 특정 희생자들을 타겟으로 이메일로 피싱 사이트 URL을 뿌렸다고 생각해봅시다. 희생자가 피싱 사이트에 접속하면 공격자가 삽입한 악의적인 스크립트를 실행됩니다. 희생자의 인증 정보가 탈취되고 정상적인 클라이언트가 아닌 외부 사이트가 마치 희생자인 것처럼 희생자의 권한을 가지고 서버에 요청을 보내게 됩니다.



이 때문에 모든 최신 브라우저는 이러한 공격의 위험성으로부터 클라이언트를 보호하기 위해 동일 출처 정책이라 불리는 보안 메커니즘을 구현하고 있습니다.

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

동일 출처 정책(Same Origin Policy, SOP)은 서로 다른 출처에서 가져온 리소스와 상호 작용할 수 있는 방법을 제한하는 보안 메커니즘입니다. 앞서 설명한 CSRF와 같이 악의적인 스크립트에 의해 허용되지 않은 클라이언트가 정상적인 사용자의 권한으로 서버에 요청을 보내 데이터에 접근하는 것을 방지합니다.

SOP에서 출처는 프로토콜, 포트, 호스트에 의해서 정의됩니다. 예를 들면 "https://shoutlink.me" 라는 URL이 있다고 하면 다음과 같습니다.

URL 동일한 출처인가?
https://shoutlink.me/hubs/1 동일한 출처
http://shoutlink.me 프로토콜과 포트가 다름(http의 기본 포트는 80)
https://api.shoutlink.me 도메인이 다름
https://shoutlink.me:3000 포트가 다름

이러한 동일 출처 정책으로 인해 처음 웹 어플리케이션을 개발하고 프론트엔드와 백엔드를 연결하려는 감격스러운 순간 CORS 에러를 만나게 됩니다. 새내기 백엔드 개발자는 부랴부랴 다른 출처 간의 접근을 허용하기 위해 CORS를 이용하는 방법을 찾아보게 됩니다.



CORS는 어떻게 작동하는가?

CORS는 HTTP 헤더를 사용하여 출처가 다르더라도 응답을 주고받을 수 있도록 서버에 리소스 호출이 허용된 출처(Origin)을 명시합니다. 이를 통해 웹 애플리케이션은 리소스가 자신의 출처와 다르더라도 교차 출처 HTTP 요청을 실행하게 됩니다.

브라우저는 OPTIONS 메서드로 프리플라이트(preflight) 요청을 통해 지원하는 메서드를 요청하고 서버의 허가가 떨어지면 실제 요청을 보내도록 요구하고 있습니다. 일부 단순 요청의 경우에는 CORS 프리플라이트를 트리거하지 않으나 웹 어플리케이션을 개발한다면 대부분의 요청이 프리플라이트를 트리거하게 됩니다.



프리플라이트 요청

Origin

  • 요청이 시작된 서버를 나타내는 URI입니다. 경로 정보를 제외한 서버 이름만 포함합니다. 접근 제어 요청 시 항상 전송되는 헤더입니다.
  • 예시 헤더의 경우 요청이 시작된 서버 URL는 https://www.shoutlink.me입니다.

Access-Control-Request-Method

  • 실제 요청에서 어떤 HTTP 메서드를 사용할지 서버에 알려주기 위해 사용되는 헤더입니다.
  • 예시 헤더의 경우 실제 요청에 GET 메서드가 사용된다고 알려주고 있습니다.

Access-Control-Request-Headers

  • 실제 요청에서 어떤 HTTP 헤더를 사용할지 서버에게 알려주기 위해 사용되는 헤더입니다.
  • 예시 헤더의 경우 실제 요청에 authorization 헤더가 사용된다고 알려주고 있습니다.

프리플라이트 응답

Access-Control-Allow-Origin

  • 해당 출처의 요청이 리소스에 접근하도록 허용합니다.
  • 와일드카드(*)를 사용하는 경우 모든 출처의 요청을 허용할 수 있지만 보안상 취약하기 때문에 단일 출처를 지정하는 방법이 더 좋습니다.
  • 서버가 단일 출처를 지정하는 경우 Vary 응답 헤더에 Origin을 포함해야 합니다. 해당 헤더의 값은 요청 출처에 따라 동적으로 변경될 수 있으며 서버 응답이 Origin 요청 헤더에 따라 다르다는 것을 클라이언트에 알려줍니다.
  • 예시 응답 헤더의 경우 https://www.shoutlink.me의 요청을 허용하고 있습니다.

Access-Control-Allow-Methods

  • 리소스에 접근할 때 허용되는 메서드를 지정합니다.
  • Access-Control-Allow-Origin과 동일하게 와일드카드를 사용할 수 있습니다.
  • 예시 응답 헤더의 경우 GET, POST, PATCH, DELETE, OPTIONS를 허용하고 있습니다.

Access-Control-Allow-Headers

  • 실제 요청시 사용할 수 있는 HTTP 헤더를 나타냅니다.
  • 예시 응답 헤더의 경우 authorization 헤더를 허용하고 있습니다.

Access-Control-Allow-Credentials

  • 실제 요청시 credentials를 사용하여 요청을 수행할 수 있는지를 나타냅니다.
  • 해당 헤더의 값이 true로 설정되어 있지 않으면 쿠키, authorization 헤더, TLS 클라이언트 인증서가 포함된 요청을 수행할 수 없습니다.

Access-Control-Max-Age

  • 프리플라이트 요청 결과를 캐시할 수 있는 시간을 나타냅니다.

참고

https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy
https://aws.amazon.com/ko/what-is/cross-origin-resource-sharing/
https://en.wikipedia.org/wiki/Same-origin_policy