ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • CORS(Cross Origin Resource Sharing)
    Web/Network 2021. 5. 26. 03:42
    반응형

    도메인이나 서브도메인, 프로토콜, 포트가 다른 곳에 보내는 요청을 'Cross-origin 요청'이라고 한다.

    Cross-origin 요청을 보내려면 리모트 오리진(주로 서버)에서 전송받은 특별한 header가 필요하다.

    이러한 정책을 바로 CORS(Cross Origin Resource Sharing)라고 한다.

     

    HTTP 요청은 원래 Cross-origin 요청을 보내는 것이 가능하다.

    <script> 태그나 <link> 태그 등을 사용하면 다양한 출처의 리소스를 불러올 수 있다.

    <script src="http://another-origin.com">... 의 형태로 데이터를 요청하면

    JSONP(JSON with padding)라는 프로토콜을 사용해서 데이터를 가져오게 된다.

    <script> 태그의 src 속성에는 도메인 제약이 없다는 점을 이용하는 것.

    그래서 jQuery에는 이 JSONP를 이용해서 데이터를 가져오는 메소드도 존재한다.

     

    그러던 와중에 자바스크립트에 HTTP 요청을 보내는 메서드가 등장했다.

    원래는 <script> 태그 내부에서 메서드로 생성된 HTTP 요청은

    동일 출처 원칙(Same Origin Policy)를 적용받으므로 Cross-origin 요청이 불가능했다.

    하지만 긴 논의 끝에, 서버에서 명시적으로 Cross-origin 요청을 허가했다는 내용을 포함한

    특별한 header를 전송받았을 때에 한해 Cross-origin 요청을 허가하도록 결정되었다.

     

    Cross-origin 요청은 크게 두 가지로 구분된다.

    안전한 요청과 안전하지 않은 요청.

     

    안전한 요청(Simple Request)

    안전한 요청은 다음의 두 가지 조건을 모두 충족하는 요청을 의미한다.

    1. '안전한 메서드(GET, POST, HEAD)'를 사용한 요청

    2. '안전한 헤더'에 속하는 헤더를 가진 요청

      - Accept

      - Accept-Language

      - Content-Language

      - Content-Type이 application/x-www-form-urlencoded, multipart/form-data, text/plain 중 하나

     

    이 두 조건을 모두 충족하지 않는, 예를 들면 PUT 메서드를 사용한다거나

    헤더에 API Key가 명시된 요청같은 경우는 전부 안전하지 않은 요청으로 취급된다.

     

    안전한 요청은 'Origin' 이라는 헤더에 오리진(도메인, 프로토콜, 포트) 정보가 담겨서 전송된다.

    만약 서버에서 요청을 받아들이기로 했다면, 서버는 Access-Control-Allow-Origin 이라는 헤더에

    요청받은 오리진이나 *를 추가해서 응답을 보내야 한다.

     

    그러면 브라우저가 이 응답을 낚아채서

    Access-Control-Allow-Origin 헤더를 보고 현재 오리진과 일치하는지 검사한다.

    만약 응답에 해당 정보가 들어있으면 응답은 성공하고, 그렇지 않으면 응답은 실패한다.

     

    1. 브라우저는 Cross-Origin 요청 시에 Origin에 값이 제대로 설정, 전송되었는지 확인한다.

    2. 브라우저는 서버로부터 받은 응답에 Access-Control-Allow-Origin이 있는지를 확인해서 

        서버가 Cross-Origin 요청을 허용하는지 아닌지를 판단한다. 응답 헤더에 Access-Control-Allow-Origin이 있다면

        자바스크립트를 사용해서 해당 응답에 접근할 수 있고, 그렇지 않다면 브라우저가 에러를 발생시켜 접근을 막는다.

     

    안전하지 않은 요청(Prefilght Request)

    안전한 요청 이외의 모든 요청.

    즉 PUT, DELETE, PATCH 등을 사용한다거나 하면 전부 안전하지 않은 요청으로 취급된다.

    브라우저가 안전하지 않은 요청을 서버에 보내기 전에는 'preflight' 요청을 먼저 전송한다.

    preflight 요청은 HTTP 요청 메소드 중 'OPTIONS' 메소드를 사용한다.

     

    서버는 이 preflight 요청에 대한 응답으로 자신이 '어떤 것들을 허용하는지',

    '어떤 것들을 금지하는지'에 대한 정보를 응답 헤더에 담아서 브라우저에게 다시 보내준다.

     

    그러면 브라우저는 자신이 보낸 preflight 요청과 서버가 응답에 담아준 허용 정책을 비교한 후, 

    해당 요청을 보내는 것이 안전하다고 판단되면 그제서야, 동일한 endpoint로 본 요청을 보낸다.

     

    만약 preflight에 대한 응답으로 서버가 Cross-origin 요청을 허용하지 않는다는 정보를 담은 header를

    브라우저에게 보내주면 안전하지 않은 요청은 서버로 전송되지 않는다.

     

    이후에 서버가 본 요청에 대한 응답을 보내주면 브라우저는 최종적으로 해당 응답을 자바스크립트에게 넘겨준다.

     

    사용되는 특수 헤더

    - Access-Control-Allow-Methods: 본 요청에서 사용하는/허용하는 메서드

    - Access-Control-Allow-Headers: 본 요청에서 사용하는/허용하는 안전하지 않은 헤더 목록

    - Access-Control-Max-Age

     

    인증 정보(자격 증명)를 포함한 요청(Credentialed Request)

    브라우저가 기본적으로 제공하는 비동기 요청 방법인 XMLHttpRequest나 fetch API를 사용해서 요청을 보낼 때, 

    별도의 옵션이 없으면 브라우저가 지니고 있는 쿠키나 인증과 관련된 헤더(자격 증명)를 함부로 요청에 담지 않는다.

    이 때, option에 { credentials: "include" } 를 포함시켜 보내면 요청에 인증과 관련된 정보를 담을 수 있게 된다.

     

    만약 자격 증명 정보가 담긴 요청을 서버에서 받아들이기로 동의했다면,

    서버는 응답에 Access-Control-Allow-Origin 헤더와 함께

    Access-Control-Allow-Credentials: true 헤더를 추가하여 보내야 한다.

    이 때, Access-Control-Allow-Origin 헤더에는 반드시 *이 아닌 정확한 오리진 정보가 명시되어야 한다.

    만약 자격 증명 정보가 포함된 요청에 대한 응답의 Access-Control-Allow-Origin이 *이면,

    브라우저는 이를 거부하여 응답이 정상적으로 도착하지 않는다.

     

    이러한 제약들이 있어야, 어떤 오리진에서 요청이 왔는지에 대한 정보를 서버가 신뢰할 수 있기 때문이다.

     

    CORS 에러를 해결하는 방법

    1. 동일한 오리진을 사용하기

    애초에 CORS 에러 자체가 발생하지 않는다.

     

    2. 서버에서 Access-Control-Allow-Origin 세팅하기

    그냥 정석대로 응답 헤더에 Access-Control-Allow-Origin* 이나 '허용하고자 하는 오리진'으로 설정해준다.

    참고로 * 을 사용하면 모든 출처에서 오는 요청들을 전부 허용하기 때문에 편할 순 있지만,

    정체를 모르는 이상한 출처에서 오는 요청까지도 전부 허용하기 때문에 보안적으로 굉장히 좋지 않다.

     

    3. 클라이언트에서 프록시 서버 이용하기

    본인이 직접 서버 헤더를 요청할 수 있는 입장이 아니라면,

    서버와 브라우저 사이에 프록시 서버를 하나 거쳐서 요청과 응답을 주고받을 수 있도록 하면 된다.

    프록시 서버는 말 그대로 서버와 브라우저 사이에서 데이터 교환을 도와주는 '중간 서버'의 역할을 한다.

     

    중간에서 프록시 서버를 이용해서 요청을 허용하거나 거부할 수 있고 헤더를 추가할 수도 있어서

    Access-Control-Allow-Origin: * 를 헤더에 담아서 응답하도록 만들어줄 수 있다.

     

     

    참고로, CORS는 웹이 아니라 브라우저에서 제공하는 규칙이다.

    요청에 의해 서버에서 주는 응답을 브라우저가 중간에 낚아채서 CORS 규칙을 만족하는지 검사한다.

    만약 CORS 규칙에 어긋나는 경우에는 CORS 에러를 발생시켜 애플리케이션이 정상적인 응답을 받지 못하게 한다.

     

    즉, 서버에서는 정상적으로 응답했다고 표시되더라도

    정작 애플리케이션에서는 CORS 에러로 인해 정상적인 응답을 받지 못했을 수 있다는 것.

     

    또한, 브라우저를 통하지 않는 서버간 통신의 경우 CORS 정책이 적용되지 않는다.

     

     

     

    참고

     

    fetch와 Cross-Origin 요청

     

    ko.javascript.info

     

    교차 출처 리소스 공유 (CORS) - HTTP | MDN

    교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라

    developer.mozilla.org

     

    CORS는 왜 이렇게 우리를 힘들게 하는걸까?

    이번 포스팅에서는 웹 개발자라면 한번쯤은 얻어맞아 봤을 법한 정책에 대한 이야기를 해보려고 한다. 사실 웹 개발을 하다보면 CORS 정책 위반으로 인해 에러가 발생하는 상황은 굉장히 흔해서

    evan-moon.github.io

     

    반응형

    'Web > Network' 카테고리의 다른 글

    S.W.R = Stale-While-Revalidate  (0) 2022.02.05
    채팅을 위한 WebSocket 사전 공부  (0) 2021.08.19

    댓글

Designed by Tistory.