고양이와 코딩하기

(CORS) Cross-Origin Resource Sharing 본문

D E V E L O P E R 💻/Today I Learned

(CORS) Cross-Origin Resource Sharing

dj-1087 2024. 8. 21. 18:40
반응형

프론트엔드 개발 당시 개발자 도구 콘솔창으로 CORS관련 빨간 메시지가 뜬 적이 정말 많았다.

당시에는 그때그때 마다 구글링을 통해 주먹구구식으로 해결했었고, 그런 과정이 반복되면서 자연스럽게 CORS가 무엇인지 어렴풋이 알게되었다.

요즘 시큐리티 공부 관련으로 CORS 다시 보게되면서 이번 기회에 자세히 정리해보려고 한다.

목차

  1. CORS란?
  2. CORS 접근 시나리오
    1. Simple Request
    2. Preflight Request + Requests with credentials
  3. 주의사항
    1. 자격증명이 포함된 요청에서의 와일드카드 제한
    2. 서드 파티 쿠키

 

1. CORS란?

브라우저는 보안상 기본적으로 SOP(Same Origin Policy, 동일 출처 정책)가 적용된다. 즉, 브라우저에서 A라는 사이트를 통해 B 서버에 요청을 보내면 기본적으로 막힌다는 것이다. 

하지만, 웹개발 기술 및 방법론이 발달함에 따라 프론트엔드와 백엔드를 분리하는 경우가 많아졌고, 이뿐만 아니라 Open API 등 다른 여러 사이트에서 유용하게 이용할 수 있는 API 서비스들이 많이 생겨나면서, 사실상 SOP를 지키기 어려운 상황이 많이 발생했다.

CORS란 브라우저가 자신의 Origin(웹페이지의 출처, ex> domain or ip:port)이 아닌 다른 어떤 Origin으로 부터 자원을 로딩하는 것을 허용하도록 서버가 허가해주는 HTTP Header 기반 매커니즘이다.
따라서 cors는 실제 요청을 허가할 것인지 확인하기 위해서 브라우저가 보내는 Preflight Request(사전 요청) 메커니즘에 의존한다.

CORS 표준은 서버가 HTTP Response header에 웹 브라우저가 해당 정보를 읽는 것이 허용될 수 있는 조건(Access-Control-Allow-Origin 등)을 서술함으로써 동작한다. 또한 서버 데이터에 영향을 줄 수 있는 요청의 경우(GET이외의 요청), CORS 명세는 브라우저가 HTTP OPTIONS 메서드로 해당 request url에 대해 서버에서 지원하는 메서드들을 요구하는 “Preflight Request”를 보내고, 서버로부터 승인 받으면 실제 요청을 보낸다.
CORS 실패는 오류를 발생시키지만, 보안상의 이유로 오류에 대한 세부 사항은 JavaScript에 제공되지 않는다. 구체적으로 무엇이 잘못되었는 지를 확인하려면 브라우저의 콘솔에서 세부사항을 살펴볼 수 있다.

2. CORS 접근 제어 시나리오

2.1 Simple Request

  • 앞서 말한 preflight request를 유발하지 않는 단순한 요청을 말한다. 서버에 바로 실제 요청을 보내고 서버는 CORS 허용 조건(Access-Control-Allow-Origin 등)을 헤더에 붙혀서 응답을 보내주면 브라우저가 정책 위반 여부를 검사한다.
    * 아래 예시는 RHEL8 기준

*단순 요청을 충족하는 조건

더보기
  • HTTP Method: GET, HEAD, POST
        *HEAD: Response header 요청하는 메소드
  • User Agent 자동으로 설정한 헤더 외에, 수동으로 설정할 있는 헤더 제한(Fetch 명세에서 "CORS-safelisted request-header" 정의한 헤더 참고): Accept, Accept-Language, Content-Language, Content-Type, Range
  • Content-Type 제한
        - application/x-www-form-urlencoded
        - multipart/form-data
        - text/plain

 

Rest API에서 파일 업로드(multipart/form-data)외에는 보통 application/json 사용하기 때문에 거의 대부분은 Preflight Request 보낸다.

2.2 Preflight Request + Requests with credentials

  • Preflight Request: 서버 데이터의 수정을 방지하기 위해 실제 요청이 안전한지 판단하기 위해 OPTIONS 메서드로  사전 요청을 보내는 것을 의미한다.
  • Requests with credentials: HTTP 쿠키와 HTTP 인증정보를 이용한 “자격증명이 포함된” 요청을 하는 것을 의미한다.

CORS 사전 요청에는 자격 증명이 절대로 포함되지 않아야 한다. 사전 요청에 대한 응답은 실제 요청이 자격 증명과 함께 수행될 수 있음을 나타내기 위해 Access-Control-Allow-Credentials: true 를 명시해야 한다.

 

(CORS 예제) 로그인 요청

통상적인 로그인을 위한 POST 요청이며 403 에러로 실패한 예제(패스워드 잊어버림..)이다.

보면 login이라는 요청에 실제로 2 요청이 보내진 것을 있고, 요청은 preflight 타입이라고 되어있다.

 

(CORS 예제) Preflight Request - General

Preflight 요청을 자세히 살펴보자, Request Method OPTIONS 되어 있고, Status Code 200 것으로 보아 CORS 허용 조건에 맞아 서버가 요청을 수락한 것을 있다. 

 

(CORS 예제) Preflight Request - Request Headers

다음으로 Request Headers를 보면 Access-Control-**로 되어 있는 2가지 속성이 있다.

Access-Control-Request-Method 헤더는 Preflight 요청의 일부로써 서버에게 실제 요청이 전송될  POST 요청 메서드를 사용할 것임을 알려준다.  

Access-Control-Request-Headers 헤더는 실제 요청이 전송될 Content-Type  사용할 것임을 서버에게 알려준다. 서버는 이러한 조건 하에서 요청을 수락할 있는지 여부를 결정한다.

 

(CORS 예제) Preflight Request - Response Headers

마지막으로 Response Headers를 보면 Access-Control-**로 되어 있는 6가지 속성이 있다.

 

Access-Control-Allow-Credentials은 브라우저에서 프론트엔드 자바스크립트가 자격증명(쿠키, authorization 헤더들 또는 TLS 클라이언트 인증서)에 대해 접근 가능하도록 허용여부를 설정하는 옵션이다. cross origin 요청을 위해서는 API 서버에서 Access-Control-Allow-Credentials를 true로 설정해주어야할 뿐만 아니라, 요청하는 클라이언트의 프론트엔드 측에서도 Request.credentials을 “include”로 설정해주어야한다.

더보기

*Request.credentials: Fetch API의 Request 인터페이스의 credentials은 user agent가 다른 도메인으로 cookie들을 전송할지 여부에 대한 설정이다.

  • omit: 절대로 cookie 들을 전송하거나 받지 않는다.
  • same-origin: 요청 URL이 호출 script 와 동일 출처(same origin)에 있다면, user credentials을 전송한다. 이것은 default 값이다.
  • include: cross-origin 호출이라 할지라도 언제나 user credentials을 전송한다.

Access-Control-Allow-Origin은 서버에서 CORS를 허용하는 Origin에 대한 설명이고, 

Access-Control-Allow-Methods는 해당 요청 경로(여기에서는 “/user/login”)에 대해 유효한 HTTP Method 종류이다. 

Access-Control-Allow-Headers는 실제 요청에 사용할 수 있는 허용된 헤더임을 확인해준다. 

Access-Control-Expose-Headers 헤더를 통해 서버는 cross-origin 요청에 대한 응답으로 브라우저에서 실행 중인 스크립트가 사용할 수 있는 응답 헤더를 지정할 수 있다. 기본적으로는 Fetch 명세에서 "CORS-safelisted request-header"로 정의한 헤더들만 노출된다.

Access-Control-Max-Age  다른 사전 요청을 보내지 않도록 사전 요청에 대한 응답을 얼마나 오래동안 캐시할 있는지 서버에서 설정한 단위 시간 값이다. 최대 캐시시간은 86400(=24시간)이다.

3. 주의사항

3.1 자격증명이 포함된 요청에서의 와일드카드 제한

  • 서버는 Access-Control-Allow-Origin 응답 헤더 값으로 "*" 와일드카드를 지정해서는 안 되며, 대신 명시적인 출처를 지정해야 한다. 예> Access-Control-Allow-Origin: https://example.com.
  • 서버는 Access-Control-Allow-Headers 응답 헤더 값으로 "*" 와일드카드를 지정해서는 안 되며, 대신 명시적인 헤더 이름 목록을 지정해야 한다. 예> Access-Control-Allow-Headers: X-PINGOTHER, Content-Type.
  • 서버는 Access-Control-Allow-Methods 응답 헤더 값으로 "*" 와일드카드를 지정해서는 안 되며, 대신 명시적인 메서드 이름 목록을 지정해야 한다. 예> Access-Control-Allow-Methods: POST, GET.
  • 서버는 Access-Control-Expose-Headers 응답 헤더 값으로 "*" 와일드카드를 지정해서는 안 되며, 대신 명시적인 헤더 이름 목록을 지정해야 한다. 예> Access-Control-Expose-Headers: Content-Encoding, Kuma-Revision.

요청에 자격 증명(가장 일반적으로는 Cookie 헤더) 포함되고 응답에 Access-Control-Allow-Origin: * 헤더(, 와일드카드) 포함되어 있으면, 브라우저는 응답에 대한 접근을 차단하고 개발자 도구 콘솔에 CORS 오류를 보고한다. 응답의 Access-Control-Allow-Origin 헤더 값에 실제 출처가 아닌 "*" 와일드카드인 경우 응답의 Set-Cookie 헤더는 쿠키를 설정하지 않는다.

3.2 서드 파티 쿠키

CORS 응답에 설정된 쿠키는 일반적인 서드 파티(third-party) 쿠키 정책의 적용을 받는다. 위의 예에서, 페이지는 foo.example 에서 로드되지만, 응답의 Cookie 헤더는 bar.other 에서 전송되므로 사용자의 브라우저가 모든 서드-파티 쿠키를 거부하도록 설정된 경우 해당 쿠키는 저장되지 않는다.

요청의 쿠키도 일반적인 서드-파티 쿠키 정책에 따라 억제될 있다. 따라서 강제된 쿠키 정책은 장에서 설명된 기능을 무효화 있으며, 자격 증명이 포함된 요청을 전혀 수행할 없게 만들 있다.

 

참고자료

 

Cross-Origin Resource Sharing (CORS) - HTTP | MDN

Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources. CORS also relies on a mechanism by which

developer.mozilla.org

 

 

반응형
Comments