CORS와 동일 출처 정책 이해하기: Zoom 취약점에서 얻은 교훈
핵심 요점: CORS는 서버를 위한 보안 도구가 아니다
Cross-Origin Resource Sharing (CORS)은 서버에 대한 요청을 차단하는 메커니즘이 아니라, 브라우저 측 메커니즘으로 서버가 동일 출처 정책 (SOP)을 완화하여 프런트엔드 애플리케이션이 응답을 읽을 수 있게 해준다. 이 구분을 오해하면 개발자들이 CORS를 우회하기 위해 "해킹"을 시도하거나 CORS 헤더가 백엔드를 무단 요청으로부터 보호한다고 잘못 믿게 된다.
사례 연구: Zoom 로컬호스트 취약점
2019년 Zoom에서 애플리케이션이 localhost:19421에 웹 서버를 실행해 Zoom 웹사이트가 네이티브 앱을 트리거하도록 하는 취약점이 발견되었다. 이 로컬 서버에 AJAX 요청을 보낼 때 CORS 제한을 우회하기 위해 Zoom은 이미지 해킹을 구현했으며, 데이터가 이미지 파일의 차원에 인코딩되었다.
이미지 해킹이 보안 구멍을 만든 이유
브라우저는 일반적으로 AJAX 요청과 달리 이미지 로딩을 출처 간에 제한 없이 허용하기 때문에, 이 우회는 동일 출처 정책이 제공하는 보호를 사실상 무력화한다. 결과적으로 인터넷상의 어떤 웹사이트든 zoom.us만이 아니라 로컬 Zoom 서버에 요청을 보내고 이미지 차원을 통해 응답을 읽을 수 있게 되어, 네이티브 클라이언트에서 무단 작업이 수행될 가능성이 생겼다.
올바른 구현 방법
이 기능을 안전하게 만들려면 Zoom은 로컬 서버에 REST API를 구현하고 Access-Control-Allow-Origin 헤더를 https://zoom.us 로 정확히 설정했어야 한다. 이렇게 하면 공식 Zoom 웹사이트는 응답을 읽을 수 있지만, 다른 악의적인 사이트는 이를 차단할 수 있다. 또한 iframe 렌더링을 차단하는 Content Security Policy (CSP)를 적용하면 Zoom 사이트가 악성 프레임에 삽입되어 백그라운드 동작을 트리거하는 것을 방지할 수 있다.
SOP vs. CORS: 혼란 해소
개발자들의 혼란 대부분은 동일 출처 정책 (SOP)과 Cross-Origin Resource Sharing (CORS)을 구분하지 못해서 발생한다.
동일 출처 정책 (SOP)
SOP는 주요 보안 경계이다. 하나의 출처(프로토콜, 호스트, 포트)에서 온 문서가 다른 출처에 속한 데이터를 읽는 것을 방지한다. 예를 들어, SOP는 malicious-site.com이 로그인된 상태에서 gmail.com에 있는 개인 이메일을 가져오는 것을 막는다.
Cross-Origin Resource Sharing (CORS)
CORS는 SOP를 완화하는 메커니즘이다. 서버가 브라우저에 명시적으로 "이 특정 출처를 신뢰하므로 해당 출처에서 실행되는 JavaScript가 이 요청의 응답을 읽을 수 있다"고 알려준다.
주요 기술적 구분
- 쓰기 vs. 읽기: SOP는 일반적으로 교차 출처 응답의 읽기를 차단하고, 요청 전송 자체는 차단하지 않는다. 요청은 서버에 전송·처리될 수 있지만(CORS 헤더가 없으면) 브라우저는 JavaScript가 결과를 읽는 것을 차단한다.
- 강제 지점: CORS는 완전히 브라우저에 의해 강제된다.
curl이나 Postman 같은 비브라우저 클라이언트는 CORS 헤더를 전혀 무시하므로 서버에 대한 보호를 제공하지 않는다.
개발자들이 CORS에 어려움을 겪는 이유
개발자들 사이의 기술 토론에서는 CORS가 지속적인 혼란의 원인으로 작용하는 몇 가지 이유가 강조된다:
- 역전된 보안 모델: 대부분의 보안 모델은 서버가 접근을 중재한다고 가정한다. CORS에서는 브라우저가 중재자가 되어 서버와 사용자를 악성 코드로부터 보호한다.
- 보이지 않는 위협 모델: CORS가 방지하는 위협은 개발 단계에서는 가상적인 경우가 많다. 개발자는 코드가 동작하도록 하려다 보니 "불편한" 오류 메시지를 마주하고
Access-Control-Allow-Origin: *같은 불안전한 기본값을 사용하게 된다. - 불명확한 오류 메시지: 보안상의 이유로 CORS 실패에 대한 브라우저 오류 메시지는 종종 의도적으로 모호하게 제공되어 디버깅이 어렵고 다른 네트워크 오류와 구분하기 힘들다.
"CORS는 기본적인 보안 형태와 본질적으로 다르다… 아이디어를 파악하려면 짧지만 신중한 읽기가 필요하다 – 이는 애플리케이션 코드를 작성하는 데만 집중하는 개발자에게는 느리게 느껴질 수 있다."
CORS 문제를 피하기 위한 모범 사례
- 리버스 프록시 사용: 백엔드를 프런트엔드와 동일 출처에 호스팅(예: 리버스 프록시 활용)하면 순수 SOP를 따르게 되어 CORS가 전혀 필요하지 않다.
- 로컬호스트 해킹 금지: 권한이 필요한 작업을 위해 이미지나 스크립트 태그를 사용해 CORS를 우회하지 말라. 로컬 서버가 필요하다면 적절한 CORS 헤더를 구현하고 서버 측에서 요청 출처를 검증하라.
- 공격자 시각으로 사고: 서버에 도달하는 모든 요청은 잠재적으로 악의적일 수 있음을 인식하라. 요청 실행을 방지하기 위해 CORS에 의존하지 말고, 모든 요청에 대해 적절한 인증·인가 토큰을 사용하라.
요약: Cross-Origin Resource Sharing (CORS)와 동일 출처 정책 (SOP)에 대한 근본적인 오해는 Zoom 로컬호스트 웹 서버 결함이 보여주듯 심각한 보안 취약점을 초래할 수 있다.
제목: CORS와 동일 출처 정책 이해하기: Zoom 취약점에서 얻은 교훈