웹 브라우저의 방어정책(SOP, CORS)

민준·2025년 1월 8일
post-thumbnail

웹 브라우저는 언제든지 JS 를 호출할 수 있고, 프로그램을 설치해야 무엇인가를 실행할 수 있는 프로세스와 달리 JS 는 꼭 굳이 자바스크립트 파일이 없더라도 <script> 태그 안에 몇 문장만 작성하면 바로 실행할 수 있다. 어떤 검증도 없이 바로 자바스크립트를 수행할 수 있기 때문에 웹 브라우저는 많은 해커들로부터 공격의 대상이 되기 쉽고 어떻게든 악의적인 JS 를 수행하게만 하면 된다.

물론 치명적인 문제를 발생시키는 JS 문법은 없지만 JS 통해 외부 API 에 요청을 보낼 수 있게 되어서, 유저가 의도적으로 API 호출한것이 아니라 악의적으로 심어진 JS 실행을 통해 API 가 호출될 수 있다.

로그인 정보 혹은 세션키들이 모두 Cookie 에 저장되어있다면 단순히 API 를 호출함으로 인해 해당 Cookie 값들이 모두 헤더에 담겨 요청이 전송되게 되고, 유저는 자신이 알지도 못하는 사이에 Cookie 에 담긴 로그인 정보를 통해 외부 API 가 자유롭게 인증정보에 대한 요구나 문제 없이 호출되게된다.

웹 브라우저의 방어정책

  1. W3C 는 가장 먼저 SOP 라는 정책 표준을 도입하여 웹 브라우저에서 단단하게 방어
  2. API 호출이 사실상 필수인 프론트엔드 개발자들로부터 온갖 욕을 먹고 추가 정책인 CORS 를 도입
    • 프론트엔드 개발자들이 자바스크립트를 통해 특정 조건하에서는 API 호출을 자유롭게 할수있게 했다

1. SOP (Same-Origin Policy)

SOP는 웹 브라우저의 기본 보안 정책으로, 서로 다른 출처(Origin) 간의 상호작용을 제한합니다. 이 정책은 웹 애플리케이션이 악의적인 코드로 인해 보안 문제를 일으키지 않도록 보호하기 위해 설계되었습니다.

1.1 Origin의 정의

  • Origin은 다음 세 가지 요소로 구성됩니다:
  1. Scheme (프로토콜): http:// 또는 https://
  2. Host (호스트/도메인명): www.example.com
  3. Port (포트 번호): 80, 443 등
  • 양쪽 URL의 Origin이 같으려면 Scheme, Host, Port가 모두 동일해야 합니다.

1.2 SOP의 작동 원리

  • 동일한 Origin에서 로드된 리소스만이 상호작용할 수 있습니다.

  • 외부 Origin에서 온 스크립트나 리소스가 민감한 데이터에 접근하지 못하도록 제한합니다.

  • 예시: Cross-Origin 발생!
    A 사이트(https://a.com)의 JavaScript가 B 사이트(https://b.com)의 데이터를 직접 요청하지 못하도록 차단.

1.3 SOP 허용 예외(Cross-Origin Embedding)

SOP는 보안을 강화하기 위해 엄격한 정책을 적용하지만, 일부 리소스는 예외적으로 허용됩니다

  • 이미지(<img> 태그)

  • 스타일시트(<link> 태그)

  • 외부 리소스 포함(<iframe> 태그)

    • 이 경우에도 데이터를 조작하거나 서버 상태를 변경하는 요청은 불가능합니다.
  • 폼(<form> 태그) -> FORM 동기 -> 호출 됨

  • 스크립트(<script> 태그) -> AJAX 비동기 -> 호출안됨


2. CORS (Cross-Origin Resource Sharing)

  • CORS는 SOP의 제약을 완화(AJAX를 사용하기 위한)하기 위한 표준으로, 특정 조건에서 다른 Origin의 리소스에 대한 접근을 허용할 수 있도록 합니다.

2.1 CORS의 필요성

SOP는 Cross-Origin 요청(예: AJAX 요청) 자체를 막아버리기 때문에, 다양한 API 호출이 필수인 현대 웹 애플리케이션에서 불편함을 초래합니다.
이를 해결하기 위해 CORS를 도입하여, 서버가 명시적으로 허용한 경우에만 Cross-Origin 요청을 허용하도록 했습니다.

2.2 CORS 작동 방식

  1. 브라우저에서 요청

    • 브라우저는 서버에 요청을 보낼 때, 해당 요청이 Cross-Origin인지 확인합니다.
    • Cross-Origin 요청의 경우, 브라우저는 서버에 "CORS 정책을 준수했는지" 확인하기 위한 추가적인 처리를 수행합니다.
  2. 서버에서 응답:

    • 서버는 응답 헤더에 특정 CORS 관련 정보를 포함시켜, 브라우저가 해당 요청을 처리할 수 있도록 허용 여부를 결정합니다.
      • 예: Access-Control-Allow-Origin 헤더에 요청 출처를 명시.
  3. 브라우저에서 검증:

    • 브라우저는 서버 응답의 CORS 헤더를 확인하여 요청의 유효성을 검증합니다.
    • 유효하지 않은 경우, 요청 결과를 폐기하고 에러를 발생시킵니다.

2.3 CORS 요청의 유형

CORS 요청은 Simple Request와 Preflight Request로 나뉩니다.

1. Simple Request(Non-Preflight)

  • 특징: 브라우저가 서버에 요청시 사전 검증 없이 바로 요청을 전송.
    요청과 응답의 CORS 정책을 요청 후 응답에서만 검증.

  • 조건: 요청이 Simple Request로 간주되기 위해 다음 조건을 충족해야함.

    • HTTP 메서드: GET, HEAD, POST 중 하나여야 함.
    • 헤더: 특정 표준 헤더만 사용 가능.(예: Content-Type, Accept, Accept-Language, DPR 등)
  • 과정:

    • 브라우저가 서버에 요청을 전송
    • 서버가 응답에 CORS 헤더를 포함
    • 브라우저는 응답의 CORS 헤더를 검증하고 처리 여부를 결정

2. Preflight Request(Preflight)

  • 특징: 요청 전에 브라우저가 OPTIONS 메서드로 "사전 요청(Preflight Reques)"을 보내서 서버가 해당 요청을 허용하는지 확인.

  • 조건:

    • HTTP 메서드: POST, PUT, PATCH 등 상태 변경 메서드.
    • 비표준 헤더(커스텀 헤더) 사용 시: 예를 들어 Authorization, X-Custom-Header 같은 커스텀 헤더.
  • 과정:

    1. 브라우저가 OPTIONS 요청을 보냅니다.
    • 헤더: Access-Control-Request-Method, Access-Control-Request-Headers
    1. 서버가 응답으로 허용된 CORS 설정을 보냅니다.
    • 헤더: Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers
    1. 브라우저가 CORS 검증을 통과하면 원 요청을 보냅니다.

2.4 CORS 설정 방법 (서버 측)

1.1 허용된 Origin

허용할 Origin 지정:
Access-Control-Allow-Origin: https://example.com

1.2 동작방식

  • 브라우저가 요청시 Origin 헤더를 보냄.
    • Origin: https://example.com
  • 서버는 응답에 Access-Control-Allow-Origin 헤더를 추가해 허용된 Origin을 명시.
    • Access-Control-Allow-Origin: https://example.com
  • 브라우저는 두 헤더를 비교, 요청이 허용된 Origin에서 왔는지 검증
    • 검증에 실패하면 브라우저는 응답을 폐기

2.1 허용된 Method

허용할 HTTP 메서드 지정:
Access-Control-Allow-Methods: GET, POST, OPTIONS

2.2 동작방식

  • 브라우저는 서버에 어떤 HTTP 메서드(GET,POST 등)를 사용할 것인지 명시.
    • 요청 헤더: Access-Control-Request-Method: POST
  • 서버는 응답으로 허용된 메서드를 명시.
    • 응답 헤더: Access-Control-Allow-Methods: GET, POST
  • 브라우저는 요청 헤더와 응답 헤더를 비교해 요청의 메서드가 허용되는지 검증.

3.1 허용된 Header

허용할 헤더 지정:
Access-Control-Allow-Headers: Content-Type, Authorization

3.2 동작방식

  • 브라우저는 요청에 사용할 커스텀 헤더를 명시.
    • 요청 헤더: Access-Control-Request-Headers: Content-Type, Authorization
  • 서버는 응답으로 허용된 헤더를 명시.
    • 응답 헤더: Access-Control-Allow-Headers: Content-Type, Authorization
  • 브라우저는 요청 헤더와 응답 헤더를 비교해, 요청의 헤더가 허용되는지 검증.

4.1 자격증명(Header 전달 허용)

인증 정보 포함 여부:
Access-Control-Allow-Credentials: true

4.2 동작방식

CORS는 기본적으로 쿠키나 인증 정보를 포함하지 않습니다. 하지만 credentials 옵션을 사용해 인증 정보를 포함할 수 있습니다.

  • 브라우저 측 설정:

    • Fetch API: credentials: 'include'
    • XMLHttpRequest: withCredentials = true
  • 서버 측 설정:

    • 응답 헤더: Access-Control-Allow-Credentials: true
  • 제약사항:

    • Access-Control-Allow-Origin 헤더를 통해 어떤 Origin에서의 요청을 허용할지 명시.
    • Access-Control-Allow-Origin 값으로 *(와일드카드)를 사용하면 모든 Origin의 요청을 허용할수 있음(인증 정보는 X, 특정 Origin 명시해야함.)
    • 하지만 Spring Security에서는 allowedOrigins(*)를 보안상(특히 인증)의 이유로 사용하지 못하게 함.
    • 대신 allowOriginPatterns을 사용하거나 아니면, 특정 Origin을 명시하여 사용할수 있음.
  • allowOriginPatterns 예시

    http.cors().configurationSource(request -> {
       CorsConfiguration config = new CorsConfiguration();
    
       // 특정 도메인 패턴 허용
       config.setAllowedOriginPatterns(List.of("https://*.example.com"));
    
       // 허용할 HTTP 메서드 지정
       config.setAllowedMethods(List.of("GET", "POST"));
    
       // 인증 정보를 허용 (쿠키, Authorization 헤더 등)
       config.setAllowCredentials(true);
    
       // 허용할 헤더 설정 (필요 시 추가)
       config.setAllowedHeaders(List.of("*"));
    
       return config;
    });
    

0개의 댓글