Single Origin Policy

Single Origin Policy

Tags
Computer Science
Network
Published
March 30, 2025
Author
Seongbin Kim
작성 중
 

1. Single Origin Policy: 동일 출처만 허용

 

1-1. 목적

  • iframe으로 외부 서비스를 로드하고, JavaScript 사용자의 정보를 추출하면, 추출이 되지 않을까요?
  • 이러한 공격을 막기 위해 브라우저는 기본적으로 교차 출처(Origin)에 대한 접근을 대부분 비허용합니다.
  • SOP는 JavaScript가 등장하면서부터 존재했던 정책으로, 1996년부터 널리 채택되었습니다.
  • ‘출처’에는 두 가지 종류가 있습니다.
    • 동일 출처(Same Origin)
    • 교차 출처(Cross Origin)
  • Origin = URI Scheme + Authority
    • URI Scheme = https://
    • Authority = Domain Name + Port
      • Domain Name = subdomain.registered-domain.tld
 

1-2. 브라우저에 의해 제한되는 Cross-Origin 접근의 종류

  • AJAX 요청
  • iframe 접근
  • Web Storage, Indexed DB
  • 타 출처의 자원을 로드하는 Canvas에 대한 JS 접근
 

1-3. Opaque Requests: 과거 시대의 교차 출처에 대한 접근 방법

  • 서드파티 자원은 과거에도 로드가 가능했습니다.
    • 단, 로드만 가능하고, 해당 자원에 대해 JavaScript로 접근할 수는 없습니다.
    • 만약 JavaScript를 로드하는 경우 실행까지 되었습니다.
 

1-4. JSONP: 과거의 SOP 회피 방법 - 서드파티 호출 방법

  • JSONP는 JSON with Padding의 약자로, 2006년부터 널리 사용되었습니다.
  • 이는 서드 파티 JavaScript를 불러오면 JavaScript가 실행되는 원리를 활용한 것입니다.
    • 호출할 함수의 이름을 전달하는 방법을 사용했습니다.
    • 서드파티 서버에서 해당 함수의 이름으로 호출하는 Script를 만들어 반환했고, 브라우저는 실행했습니다.
  • 이러한 방법은 서드 파티 API를 CORS 없이 활용하는데 사용될 수 있습니다.
    • JSONP 호출용 URL:
      • <script src="http://example.com/data?callback=handleData"></script>
    • 응답 JavaScript:
      • callback(/* …서드파티에서 제공하는 데이터 */);
  • 한계
    • JSONP는 script src를 활용했기 때문에, GET 요청만이 가능했습니다.
    • 또한 Script를 실행하기 때문에 서드 파티 서버를 온전히 신뢰해야만 했습니다.
    • CSRF 공격을 막을 수 없었기 때문에, 사용자 인증이 필요한 기능에는 사용되지 않아 단순한 정보 제공만 가능했습니다.
 

2. CORS: 예외적으로 Cross-Origin 접근 허용

 

2-1. 목적

  • CORS는 Cross Origin Resource Sharing의 약자입니다.
  • CORS는 2009년 초안이 발표되고, 2010년대 초반부터 브라우저들에서 채택되었습니다.
  • 명확한 개발 목적을 소개하는 문서는 없지만, 서드 파티 사용의 확산과 JSONP의 보안적인 한계를 개선해 Web 생태계를 더 발전시키기 위함이었을 것 같습니다.
 

2-1. 명시적 허용

  • 서버는 Response Header를 통해 명시적으로 Cross-Origin에 대한 요청과 응답 내용 조회를 허용할 수 있습니다.
  • 이를 위해 4개의 Header가 존재합니다.
    • Access-Control-Allow-
      • Origin: 특정 Origin 혹은 *
      • Methods: 명시적으로 허용할 Method 목록 (,로 구분)
      • Headers: 명시적으로 허용할 Headers 목록 (,로 구분)
      • Credentials: true 혹은 미전송
  • 서버는 위 Response Header를 Preflight 혹은 Simple Request 요청의 응답에 포함시켜 반환합니다.
  • 브라우저는 서버로부터 Access-Control-Allow Header 값에 따라서 JavaScript에서 요청을 실행하고 응답을 읽을 수 있게 허용합니다.
 

2-2. 명시적 허용 없이도 요청 가능한 Simple Request

  • 아래의 조건(CORS-safelisted)을 만족하는 요청을 Simple Request라고 합니다.
  • Simple Request는 Cross-Origin이어도 명시적 허용 없이 요청이 가능합니다.
    • Method: GET | POST | HEAD
    • Request Headers:
      • Accept, Accept-language, Content-language
      • Content-Type의 경우 form-urlencoded, multipart, text/plain일 때.
  • 단, 명시적 허용 없이도 가능한 것은 요청 뿐입니다.
    • Response Header로 허용받지 못하면 Opaque Requests로 간주되어 응답에 접근할 수 없습니다.
  • 보안 유의 사항
    • 쿠키가 포함되어도 Simple Request이기 때문에 보안에 유의해야 합니다.
 

2-3. Preflight 요청

  • Simple Request에 해당하지 않는 요청은 사전의 Preflight 요청이 요구됩니다.
  • Simple Request와는 다르게, 명시적 허용이 없다면 브라우저에 의해 요청 자체가 거부됩니다.
  • Preflight 요청은 OPTIONS 메소드와 Access-Control-Request-{Method,Headers}로 구성됩니다.
  • 서버는 Preflight 요청에 대한 캐싱 시간을 지정할 수 있습니다.
    • Access-Control-Max-Age 헤더를 활용합니다. (기본값 0, 캐싱 없음.)
      • 현실적으로 JSON API를 대부분 사용하므로 Preflight 캐싱은 사실상 필수적입니다.
 

2-4. Opaque Requests 모드

  • 현재도 CORS 등장 이전 시기와 같이 Opaque Requests가 존재합니다.
    • 대상
      • script, link, img, object/embed, font, …
  • 단, 명시적인 crossorigin=use-credentials 속성이 없다면 쿠키를 보내지 않습니다.
 
 

3. Client에서의 CORS 활용

 

3-1. HTML에서의 CORS 활용

  • 기본적으로 Opaque Requests 모드로 작동하기 때문에 CORS를 무시하고 동작합니다.
    • 이 경우, 쿠키를 전송하지 않으며, 쿠키 전송이 필요한 경우 crossorigin=use-credentials 프로퍼티를 명시해야 합니다.
    • 이 방식에서 오류가 발생하는 경우 오류 메시지 마저 ‘Script Error’라고 짧게만 제공합니다.
  • 만약 ESM(type=”module”)을 사용 중이라면 기본적으로 CORS 모드로 동작합니다.
    • Simple Request이더라도 CORS허용 응답을 받지 못하면 로드되지 않습니다.
    • 이는 ESM이 처음부터 보안에 신경을 쓰고 만들어졌기 때문입니다.
 

3-2. fetch에서의 CORS 활용

  • mode 옵션이 있습니다.
    • mode: cors | no-cors | same-origin
      • cors: CORS 요청을 그대로 사용합니다.
      • no-cors: ‘No-CORS’ 방식으로 요청 전송합니다.
        • Simple Request만 가능하며, 응답을 읽을 수 없습니다.
      • same-origin: cross-origin 요청 발생 시 throw시킵니다.
  • credentials 옵션이 있습니다.
    • credentials: include | same-origin | omit
      • 인증 정보(Cookie, Authorization 헤더)를 전송하는 cross-origin 요청을 위해서는 명시적으로 include 옵션을 사용해야 합니다.
    • 참고: Cookie 인증 방식 사용 시에는, Simple Request인 경우 Preflight가 필요 없습니다.
 

4. 주요 참고 출처

  • 도서: 프런트엔드 개발을 위한 보안 입문