티스토리 뷰

TIL(Today I Learn)

[JS] CORS 깊게 이해하기

MinsoftK 2021. 12. 28. 20:37
728x90
반응형
22.06.07 CORS 정의 수정

 

 

이전 블로그 글에서 5. HTML 임베디드 요소의 <img> 태그의 내용을 정리할 때, 어트리뷰트 중 crossorigin 어트리뷰트에 대해서 언급한 적이 있었다. 예전 면접에서 font-awesome의 아이콘을 쓰기 위해, 내 HTML에 스크립트를 추가해줘서 사용했다. 그런데 그 스크립트 태그 안에서 crossorigin이 무엇인지 설명할 수 있냐고 면접관님이 물어봤었다. CORS와 관련이 있는 것은 알았지만 CORS를 제대로 알지 못했기 때문에 설명할 수가 없었다. 그래서 굉장히 당황했던 기억이 있다.

이후 이를 이해하기 위해 많은 자료들을 참고해봤지만, 생각보다 이해가 쉽지 않았고 정확한 이해가 어려웠다. 기본이 많이 부족했기 때문에 이해가 어렵다고 생각해서 정리를 나중으로 미뤘고, 자바스크립트의 기본부터 제대로 공부를 하기 시작했다. 그리고 이제 비동기에 대한 공부를 하고 있는데, 이제야 CORS에 대한 내용을 제대로 정리할 수 있을 것 같아서 제대로 정리해보고자 한다.

 

1. CORS란?

이전에는 MDN에 교차 출처 리소스 공유 글을 읽으면서 간략하게 다른 도메인에서 자원을 요청하는 경우, 보안상의 이유로 브라우저에서 HTTP 요청을 제한한다는 직관적인 이해만으로 넘어갔다. 그래서 하나하나 정확하게 어떤 의미인지 분석해보고자 한다. 아래는 MDN에서 CORS에 대한 정의를 하고 있다.

교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다.
교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 HTTP 헤더를 기반으로한 매커니즘.

22.06.07  MDN의 한글 번역본에서는 추가 HTTP 헤더를 사용한다고 되어있는데, 영어 원문을 보면 HTTP 헤더를 기반으로한 매커니즘이라고 해석된다. 추가 HTTP 헤더란 말이 부정확하다 생각해 수정했다.

 

가장 먼저 출처란 용어에 대해서 정확한 이해가 필요할 것 같다. 출처란 사용하는 URL의 스킴(프로토콜), 호스트, 포트로 정의되는데 두 객체의 스킴, 호스트, 포트가 모두 일치하는 경우 같은 출처를 가졌다고 말한다. 

  • 스킴(프로토콜) : https
  • 호스트 : minsoftk.tistory.com
  • 포트 : http의 기본 포트는 80이다. (따로 포트를 지정하지 않은 이상 80번 포트를 사용한다)

즉 위의 3개가 같다면 같은 출처(Same-origin)이라고 한다. 아래의 두 URL은 같은 출처이다.

(https의 기본 포트는 443이다.)
https://minsoftk.tistory.com/88:443
https://minsoftk.tistory.com/70:443

 

같은 출처가 아닌 A, B가 있다고 가정했을 때, A가 B에게 자원을 요청했을 때, 추가 http 헤더를 붙여 브라우저에게 A라는 출처에서 B라는 자원을 요청할 수 있다는 것을 브라우저에게 알려줘 B의 자원을 A가 가져올 수 있게 만드는 것이 CORS이고, 다른 출처끼리 자원을 공유하기 위해 권한을 부여하는 것을 CORS라고 정리할 수 있겠다.

 

2. CORS가 필요한 이유?

먼저 위에서 출처(Origin)에 대해 이해를 할 수 있었다. 그런데 왜 교차 출처 리소스 공유(CORS)가 필요할까?라는 의문이 든다. 기본적으로 동일 출처 정책(Same Origin Policy)이라는 보안 정책이 있기 때문이다. SOP란 어떤 출처에서 불러온 문서나 스크립트가 다른 출처에서 가져온 리소스와 상호작용하는 것을 제한하는 보안 방식이다. 즉, 내 웹 페이지에서 다른 페이지의 리소스를 불러오는 것을 제한한다는 의미이다. 이런 SOP 보안 정책 때문에 CORS가 필요한 것인데, 또 SOP는 왜 필요한 것인가라는 의문이 든다.

 

3. SOP가 필요한 이유?

 글1글2에서 궁금증을 해결할 수 있었는데, 상당히 자세하게 설명이 돼 있다. 내가 이해한 바는 다음과 같다. 아래 예시는 SOP 보안 정책이 없는 경우를 가정한다.

www.gooogle.com ('o'가 하나 더 붙었을 때의 경우, 악의적인 가짜 구글 사이트라 가정)

만약 위와 같은 가짜 페이지에서 정상적인 구글 페이지와 동일하게 콘텐츠를 로드해주는 iframe이 삽입돼 있다. 이를 통해서 악의적인 가짜 페이지에서 사용자가 개인정보를 입력했을 때, 악성 페이지에서는 사용자의 개인정보를 취득할 수 있고, 악의적으로 사용될 가능성이 있다. 그래서 SOP가 필요하다는 것인데, google.com(정상적인 페이지),  gooogle.com(악성 페이지)는 호스트가 다르다. 따라서 SOP 정책으로 gooogle.com(악성 페이지)과의 상호작용을 제한한다고 이해했다.

즉, google.com으로 정상적인 접속을 했을 경우에만 SOP 정책에 위반되지 않아, google.com 서버의 자원을 요청하고 리소스를 받아올 수 있다는 이야기로 이해할 수 있을 것 같다.

 

4. crossorigin 어트리뷰트

이렇게 SOP가 왜 필요한지에 대해서 이해할 수 있었다. 그렇다면 이제 우리가 개발하는 웹 페이지에서 font-awesome의 리소스를 사용하기 위해, 추가한 스크립트 태그에서 crossorigin 어트리뷰트의 의미를 어느 정도 이해할 수 있을 것 같다. 

<script src="https://kit.fontawesome.com/(YOUR_private_key).js" crossorigin="anonymous"></script>

 

정리하면, 우리가 개발하는 웹 페이지에서 font-awesome에 자원을 요청한다. SOP 정책에 위반되기에 CORS를 허용해줘야 한다. 따라서 crossorigin 어트리뷰트를 추가해준 것으로 이해할 수 있을 것 같다. 기본적으로 crossorigin 어트리뷰트를 명시하지 않으면 CORS요청은 할 수 없다고 MDN에서 설명하고 있다. 따라서 crossorigin을 반드시 명시해줘야 하고, crossorigin 어트리뷰트 값으로 'anonymous'를 주어서 CORS를 동작하게 한 것 같다.

  • 어트리뷰트의 값으로 anonymous를 줬는데, 이는 element의 CORS 요청의 credentials flag가 'same-origin'으로 지정된다. (MDN)

그런데 credentials flag란 뭘까 의문이 생긴다... Request.credentials에 대한 이해가 필요한 것 같은데, 이를 이해하기 위해선 cookie에 대해 정확한 이해가 필요할 것 같다. 또한 XHR의 withCredentials flag와는 무엇이 다른지에 대한 이해도 필요할 것 같다. 추후에 관련 내용의 포스팅도 추가할 예정이다. 지금은 CORS의 동작 방식중 하나겠구나로 추측만 하고 넘어가려 한다. (아직 Font Awesome의 예시가 정확하게 어떤 방식으로 동작하는지 이해를 하지 못했다...)

 

5. CORS 동작 방식

CORS 동작하는 방식이 3가지가 있는데, 단순 요청(Simple Request)의 경우만을 이해해보고자 한다. https://minsoftk.tistory.com 도메인에서 임의의 웹 콘텐츠(https://foo.example)를 호출하길 원한다. 그러면 나의 웹 콘텐츠에서 아래와 같은 자바스크립트 코드가 사용될 수 있다. 

const xhr = new XMLHttpRequest();
const url = 'https://foo.example/resource/data/';

xhr.open('GET', url);
xhr.onreadystatechange = someHandler;
xhr.send();

 

그러면 브라우저가  Origin : https://minsoftk.tistory.com라는 http 헤더를 서버로 전송하고, 서버에서는 아래와 같은 Access-Control-Allow-Origin 헤더를 다시 전송해준다. Origin과 Access-Control-Allow-Origin를 이용한 CORS 방식을 단순 요청 방식이라고 한다. 해당 리소스에 대한 접근을 허용하려면, Access-Control-Allow-Origin 헤더에는 요청의 Origin 헤더에서 전송된 값이 포함되어야 한다.

Access-Control-Allow-Origin: * // 모든 도메인에서 접근할 수 있음
// or
Access-Control-Allow-Origin: https://minsoftk.tistory.com // minsoftk 도메인의 요청만 접근을 허용

 

 

6. 총 정리

간단하게 정리하자면 ajax에 주로 사용되는 XMLHttpRequest(XHR)은 전체 페이지의 새로고침없이도 URL로부터 데이터를 받아올 수 있다. 따라서 ajax 프로그래밍에 주로 사용된다. 이를 활용해 우리가 만들 웹 사이트에서 다른 출처의 자원을 새로고침없이 받아올 수 있다. 이때 CORS가 필요한데, 이를 이해하기 위해 예전에 경험했던 Font Awesome을 경우를 예시로 이해해보고자 했다. 

가장 먼저 출처라는 용어와 SOP, CORS에 대해 먼저 정리를 했다. 그리고 CORS를 단순 요청 방식을 동작시켰다. CORS의 3가지 방식 중 하나인 단순 요청의 경우를 살펴봤다. Font Awesome도 단순 요청처럼 CORS 허용을 해주는 역할로만 추측만 했을 뿐, 아직 어떤 동작인지 정확한 이해를 하지는 못했다. 이를 이해하기 위해 Cookie와 credentials flag를 정확히 알아야 하고, XHR 요청의 withCredentials flag와는 뭐가 다른 지의 구별이 필요하다. 추후에 2탄으로 정리해볼 계획이다.

 

 

혹시 틀리거나 잘못된 내용이 있다면 댓글로 알려주세요. :)

 

 

📕 Reference:

https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy

https://developer.mozilla.org/ko/docs/Web/HTTP/CORS

https://developer.mozilla.org/ko/docs/Web/API/XMLHttpRequest

https://developer.mozilla.org/ko/docs/Glossary/Origin

https://www.hackedu.com/blog/same-origin-policy-and-cross-origin-resource-sharing-cors

https://www.netsparker.com/whitepaper-same-origin-policy/

https://stackoverflow.com/questions/24687313/what-exactly-does-the-access-control-allow-credentials-header-do

728x90
반응형
댓글