---
title: "개발자들이 CORS를 대충 알면 이런 보안 사고가 난다"
published: 2026-06-21T01:35:40.000Z
canonical: https://jeff.news/article/4184
---
# 개발자들이 CORS를 대충 알면 이런 보안 사고가 난다

2019년 Zoom 취약점을 계기로, 많은 웹 개발자가 CORS를 제대로 이해하지 못한 채 우회부터 한다는 점을 짚은 글이다. localhost에 떠 있는 네이티브 앱용 웹서버가 모든 웹사이트의 요청을 받아버리면, 편의 기능이 바로 보안 취약점으로 바뀐다.

- 이 글의 출발점은 2019년 Zoom 취약점임. 핵심은 “CORS를 모르고 우회하면 편의 기능이 취약점이 된다”는 얘기임
  - Zoom은 사용자 PC의 `localhost:19421`에 웹서버를 띄워두고, Zoom 링크를 열면 웹사이트가 이 로컬 서버에 요청을 보내 네이티브 앱을 실행하게 했음
  - 문제는 이 로컬 서버가 특권 동작을 제공하는데, 웹 전체에서 접근 가능한 형태였다는 점임

- 당시 분석 중에는 “브라우저가 localhost 서버에는 CORS 정책을 무시한다”는 설명이 있었는데, 작성자는 이게 틀렸다고 지적함
  - Chrome은 localhost 웹서버에도 CORS 헤더를 존중함
  - 개발자들이 흔히 겪는 `localhost` 프론트엔드와 다른 포트의 백엔드 API 연동도 바로 이 범주임
  - Create React App 같은 환경에서 프론트와 API가 포트만 달라도 출처가 달라지고, CORS 설정이 필요함

- 작성자가 보기엔 Zoom이 AJAX 요청이 막히자 이미지 로딩으로 우회한 것으로 보임
  - 일반 요청 대신 로컬 Zoom 서버에서 이미지를 로드하고, 이미지 크기로 에러나 상태 코드를 전달하는 방식을 쓴 정황이 언급됨
  - 이러면 브라우저의 CORS 제한을 제대로 통과하는 대신, 이미지 리소스 로딩의 특성을 이용해 정보를 빼내는 구조가 됨
  - 결과적으로 Zoom 공식 사이트뿐 아니라 인터넷의 아무 웹사이트나 로컬 Zoom 서버를 건드릴 수 있는 문제가 생김

> [!WARNING]
> localhost라고 안전한 게 아님. 브라우저에서 접근 가능한 로컬 서버가 네이티브 앱 기능까지 쥐고 있으면, 그 순간 웹사이트 전체가 공격 표면이 됨.

- 안전한 구현은 의외로 정석적임. 로컬 웹서버도 REST API처럼 만들고 출처를 제한하면 됨
  - `Access-Control-Allow-Origin`을 `https://zoom.us`로 설정하면 Zoom 도메인에서 실행되는 자바스크립트만 응답을 읽을 수 있음
  - 모든 출처를 허용하는 `*` 같은 설정은 이 상황에서 사실상 문을 활짝 여는 것과 같음
  - 로컬 서버가 설치, 앱 실행, 회의 참여 같은 특권 동작을 제공한다면 출처 필터링은 필수임

- 자동 실행 문제는 CORS만으로 끝나지 않음
  - 작성자는 Zoom 페이지가 iframe 안에서 렌더링되지 않도록 Content Security Policy도 걸어야 한다고 봄
  - 그렇지 않으면 사용자가 예상하지 못한 맥락에서 Zoom 링크가 열리고, 회의 참여 같은 동작이 뒤에서 시작될 수 있음
  - 구글 Meet처럼 앱 안에서 명확한 확인 화면을 보여주는 쪽이 예측 가능한 사용자 경험에 더 가깝다는 지적도 나옴

- 이건 단순히 Zoom만의 실수가 아니라 웹 개발 현장의 반복 패턴임
  - 많은 개발자가 CORS 에러를 보안 신호가 아니라 “개발 방해물”로 취급함
  - Stack Overflow나 프레임워크 예제 중에는 그대로 복붙하면 위험한 전체 허용 설정도 흔함
  - 다른 벤더들도 Zoom과 같은 유형의 취약점으로 걸린 사례가 있다고 언급됨

- CORS를 우회하면 당장은 코드가 동작함. 근데 나중에 누군가 그 우회를 공격 경로로 바꿔버림
  - Same-Origin Policy는 웹이 서로의 데이터를 마음대로 읽지 못하게 막는 기본 경계임
  - CORS는 그 경계를 안전하게 열기 위한 표준 절차임
  - 경계가 귀찮다고 옆으로 돌아가면, 브라우저가 대신 지켜주던 보안 모델을 직접 망가뜨리는 셈임

```mermaid
sequenceDiagram
    participant 악성사이트
    participant 브라우저
    participant 로컬줌서버
    participant 줌앱
    악성사이트->>브라우저: 숨겨진 요청 또는 이미지 로드 유도
    브라우저->>로컬줌서버: localhost 요청 전달
    로컬줌서버->>줌앱: 회의 열기 같은 특권 동작 실행
    로컬줌서버-->>브라우저: 이미지 크기 등으로 상태 전달
    브라우저-->>악성사이트: 우회된 결과 관찰 가능
```

---

## 기술 맥락

- 여기서 중요한 선택은 브라우저와 네이티브 앱을 localhost 웹서버로 연결했다는 점이에요. 왜냐하면 웹페이지에서 네이티브 앱을 바로 제어하기 어렵기 때문에, 로컬에 작은 HTTP 서버를 띄워 중간 다리로 쓰는 패턴이 종종 나오거든요.

- 문제는 이 다리가 웹 전체에 노출된다는 거예요. 브라우저는 어떤 사이트에서든 `localhost`로 요청을 보낼 수 있으니, 서버가 출처를 제대로 검사하지 않으면 공격자 사이트도 같은 다리를 탈 수 있어요.

- CORS는 이 상황에서 귀찮은 설정이 아니라 접근 제어의 핵심이에요. 서버가 `Access-Control-Allow-Origin`으로 허용할 출처를 좁혀야 브라우저가 응답을 자바스크립트에 넘겨줄 수 있거든요.

- CSP까지 같이 나오는 이유는 자동 실행 흐름 때문이에요. iframe 안에서 몰래 Zoom 페이지를 열 수 있으면 사용자는 클릭 맥락을 제대로 이해하지 못한 채 앱 동작을 유도당할 수 있어서, 렌더링 위치 자체도 제한해야 해요.

- 개발팀 입장에서는 개발 편의를 위해 CORS를 넓게 열고 싶을 때가 많아요. 하지만 로컬 서버가 네이티브 권한이나 사용자 기기 기능과 연결되는 순간, 그 설정은 개발 편의가 아니라 제품 보안 설계가 돼요.

## 핵심 포인트

- Zoom은 로컬 웹서버와 브라우저를 연결하기 위해 이미지 크기로 상태를 전달하는 우회 방식을 쓴 것으로 보임
- 작성자는 Chrome이 localhost에서도 CORS 헤더를 존중한다고 반박함
- 안전한 구현은 localhost REST API가 특정 출처만 허용하도록 Access-Control-Allow-Origin을 제한하는 방식임
- 자동 회의 실행을 막으려면 CSP로 iframe 렌더링도 차단해야 한다고 제안함
- CORS를 끄거나 우회하는 예제 코드를 그대로 복붙하면 Zoom 같은 문제가 다른 서비스에서도 반복될 수 있음

## 인사이트

CORS는 귀찮은 브라우저 에러가 아니라 웹 보안 경계 그 자체다. 개발 중에 ‘일단 열어두자’로 넘긴 설정이 네이티브 앱 권한과 만나면 꽤 큰 사고로 번질 수 있음.
