SOP (단일 출처 정책)

The same-origin policy is a critical security mechanism that restricts how a document or script loaded from one origin can interact with a resource from another origin.

— Mozilla Developer Network (MDN)의 SOP 공식 정의 선언

브라우저를 켠 채로 해킹 사이트를 클릭했을 때, 해커가 내 네이버나 은행 계좌의 로그인 쿠키 정보를 가로채서 돈을 인출하는 대참사를 막기 위한 웹 생태계 최후의 수호자. 이 안전제일주의 보안 정책 덕분에 죄 없는 신입 개발자들은 매일 같이 'Access-Control-Allow-Origin' 에러 메세지를 붙들고 왜 내 서버 응답이 차단당하는지 눈물을 흘리며 밤샘 구글링을 하게 된다.(...)

1. 개요

단일 출처 정책(Same-Origin Policy). 1995년 넷스케이프 2.0에서 처음 고안된 이후, 현대 모든 웹 브라우저 보안 아키텍처의 철칙으로 자리 잡은 정책이다. 어떤 출처(Origin)에서 불러온 문서나 스크립트가, 다른 출처(Origin)의 자원 및 데이터와 상호작용하는 것을 브라우저가 기술적으로 완전 격리하고 차단하는 메커니즘을 의미한다. 여기서 출처란 프로토콜(Scheme), 도메인(Host), 포트(Port)라는 3가지 요소의 조합을 의미하며, 이 셋 중 단 하나라도 다르면 '남남(Cross-Origin)'으로 간주하여 매정하게 방어벽을 세운다.

2. SOP가 수호하는 브라우저의 평화

만약 웹 브라우저에 SOP라는 철통 방어막이 없다고 가정해 보자. 사용자가 포털 사이트에 로그인하여 금융 업무를 처리하고 있는 도중, 우연히 다른 탭에서 악의적인 스크립트가 심어진 도박 광고 사이트를 띄웠을 때 재앙이 찾아온다. 광고 사이트의 자바스크립트는 브라우저 내부 메모리를 헤집고 다니며 포털 사이트의 세션 ID가 담긴 [쿠키](cookie)나 JWT 값을 낚아채서 자기 서버로 유유히 보낼 수 있고, 심지어 강제로 송금 API를 호출할 수도 있게 된다. SOP는 브라우저 내부에서 작동하는 스크립트가 오직 자신이 태어난 고향(출처)으로만 API를 보내거나 해당 출처의 데이터 영역만 뒤지도록 사방에 격리 장벽을 설치함으로써, 이러한 크로스 사이트 요청 위조(CSRF)나 사이트 간 스크립팅(XSS) 공격에 대응하는 강력한 보안 신뢰 기반을 조성한다.1

3. SOP의 한계와 정교한 균열: CORS

그러나 현대 웹 개발 패러다임이 API 중심 아키텍처와 분산 마이크로서비스로 고도화되면서 SOP의 깐깐함은 오히려 개발의 큰 걸림돌이 되었다. 네이버 쇼핑 화면(naver.com)에서 결제 API 서버(api.pay.com)나 이미지 서버(static.img.com)로 데이터를 주고받아야 하는 합법적인 상호 작용마저도 SOP에 의해 사정없이 가로막히는 부작용이 속출했기 때문이다.이것이 보안과 편의성의 처절한 싸움이다. 이로 인해 '서로 다른 출처더라도, 서버가 허락한 안전한 대상이라면 데이터를 공유할 수 있게 하자'는 표준 예외 규칙이 설계되었는데, 그것이 바로 그 유명한 CORS(Cross-Origin Resource Sharing, 교차 출처 자원 공유) 정책이다.2 개발자들은 서버 응답 헤더에 특정 출처의 자격 증명을 열어주는 정교한 설정을 주입함으로써 SOP의 족쇄에서 평화롭게 해방될 수 있다.

4. SOP와 CORS가 만든 기적의 에러 메시지

4.1. Access to fetch at '...' has been blocked by CORS policy

수많은 프론트엔드 새내기들의 기세를 사정없이 꺾어버리는 전설의 빨간색 콘솔 에러다. 로컬에서 신나게 리액트로 화면을 짜고 백엔드 API를 호출했을 때, 콘솔창을 붉게 물들이며 등장한다. 분명 백엔드 서버 로그에는 200 OK 사인이 깔끔하게 뜨고 정상 데이터까지 처리했음에도, 브라우저가 최종 수신 단계에서 '출처가 다르므로 폐기 처분하겠다'며 중간에 채가버렸을 때의 허탈함은 이루 말할 수 없다. 정작 범인은 서버도 아니고 프론트엔드 코드도 아닌, 사용자를 보호하겠답시고 눈을 부릅뜬 브라우저 그 자체라는 사실이 아이러니를 자아낸다.(...)

5. 여담

  • HTML 태그의 영리한 예외: SOP는 모든 자원을 철저히 격리할 것 같지만, 의외로 <script src="...">, <img src="...">, <link rel="stylesheet"> 같은 HTML 태그를 통한 외부 출처 리소스 로딩은 전면 허용한다. 만약 이 이미지 태그조차 SOP를 엄격히 적용했다면, 구글이나 AWS의 CDN 서버에서 폰트나 라이브러리를 가져오는 행위 자체가 원천 불가능해 인터넷 생태계가 마비되었을 것이다.
  • 로컬 파일 시스템의 보안 구멍: 브라우저 주소창에 file:/// 형식으로 로컬 HTML 파일을 직접 열면, 구형 브라우저에서는 SOP가 제대로 작동하지 않아 로컬 드라이브의 개인 파일들이 무단 탈취당하는 취약점이 존재했다. 오늘날 크롬이나 파이어폭스 등 현대 브라우저들은 로컬 파일조차 각각 독립된 고유 출처로 취급해 철저히 방어하고 있다.
  • SOP 우회의 꼼수, JSONP: CORS 표준이 제정되기 전 고대 웹 개발자들은 SOP를 우회하기 위해 외부 스크립트 태그에는 출처 제한이 없다는 점을 악용한 JSONP(JSON with Padding)라는 편법을 애용했다. 백엔드가 데이터를 함수 호출문 형태의 자바스크립트 파일로 동적 생성해 반환하는 방식으로, 보안 구멍을 스스로 파내는 기괴한 아키텍처였으나 당시에는 소중한 한 줄기 빛과 같았다.

6. 관련 문서

각주

  1. SOP는 오직 '웹 브라우저' 안에서만 동작하는 규칙이다. 즉, 포스트맨(Postman)이나 curl 명령어처럼 브라우저 밖에서 API를 직접 호출하는 경우에는 출처가 무의미하므로 SOP 차단 필터가 전혀 작동하지 않는다.

  2. CORS 에러를 해결한답시고 백엔드 서버의 Allowed Origins 설정을 모든 와일드카드인 '*'로 박아두는 개발자들이 넘쳐나는데, 이는 브라우저의 피눈물 나는 방어선을 스스로 해체해 주어 해커들에게 하이패스를 깔아주는 이적 행위나 다름없다.