TIL 26. What is CORS?

Drageon Lee·2022년 1월 3일
0

CS

목록 보기
3/9

Today's topic

이번 post에서는 CORS에 대해서 다루어 보고자 한다. Django로 API를 작성하면서 매번 초기 setting을 할 때

pip install django-cors-headers

로 django-cors-headers를 설치하고 settings.py file에 해당 내용에 대해 업로드를 하고 시작하였다. 왜 매번 cors-headers를 설치해야 되는 지 궁금증이 생겨서 알아보았고 posting으로 정리해 보고자 한다.

👉 What is CORS?

CORS(Cross-Origin Resource Sharing)란 추가 HTTP header를 사용하여, 한 출처에서 실행 중인 web application이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제

👉 What is 'origin(출처)'?

Origin(출처)란 protocol, host 그리고 port를 포함한 것을 의미한다.
ex) https:// www.google.com/search?q=cors
protocol, host, port(:443) - 보이지 않음

동일 출처(same-origin)란 protocol, host, port가 같은 경우를 의미한다.

👉 What is 'SOP(same-origin policy)'?

이를 바탕으로, SOP(same-origin-policy)라는 규칙이 있다. 같은 출처(origin)에서만 리소스를 공유할 수 있다는 규칙이다. 브라우저에서 다른 서버에서 요청할 경우에 해당되고, 브라우저를 거치지 않고 서버 간 통신을 할 때는 이 정책이 적용되지 않는다.

👉 Why is SOP necessary?

가장 큰 이유는 보안상의 이유이다. 만약 이러한 제약이 있지 않을 경우, 다른 출처에서 악의를 가진 사용자가 소스 코드를 보고 CSRF(Cross-Site Request Forgery) 나 XSS(Cross-Site Scripting) 와 같은 방법을 사용하여 정보를 탈취할 수 있다. 이 것을 막기 위해 SOP와 같은 규칙이 있다.

하지만 점점 다른 출처에서 리소스를 필요로 하는 경우가 많아 지는데, 이런 경우를 위해 SOP를 우회하는 경로로 CORS가 적용이 되어 다른 출처에서 리소스를 요청하게 된다.

👉 Principle of CORS?

웹 클라이언트 어플리케이션이 다른 출처의 리소스를 요청할 때는 HTTP 프로토콜을 사용하여 요청을 보내게 되는데, 이때 브라우저는 요청 헤더에 Origin이라는 필드에 요청을 보내는 출처를 함께 담아보낸다.

ex) Origin : http://www.google.com

이후 서버가 이 요청에 대한 응답을 할 때 response header의 Access-Control-Allow-Origin이라는 값에 “이 리소스를 접근하는 것이 허용된 출처”를 내려준다.

이후 응답을 받은 브라우저는 자신이 보냈던 요청의 Origin과 서버가 보내준 응답의 Access-Control-Allow-Origin을 비교해본 후 이 응답이 유효한 응답인지 아닌지를 결정한다.

기본적으로 이러한 흐름이지만, CORS 접근 제어 시나리오 (교차 출처 리소스 공유가 동작하는 방식)에는 크게 세가지가 있어 각 경우에 따라 시나리오에 맞게 진행된다.
Request는 아래와 같이 세가지이고, 각 request에 대해서 자세히 알아 보겠다.

  • Preflight request
  • Simple Request
  • Credentialed request

👉 Preflight request

Preflight Request는 예비 요청과 본 요청으로 나뉜다. 먼저 OPTIONS 메서드를 통해 다른 도메인의 리소스로 HTTP 요청을 보내 실제 요청이 전송하기에 안전한지 확인한다. Cross-origin 요청은 유저 데이터에 영향을 줄 수 있기 때문에 이와같이 미리 전송(preflighted)한다.

  • Preflight request
    OPTIONS 요청과 함께 두 개의 다른 요청 헤더가 전송된다. 위 그림에서 Preflight request의 'Access-Control-Request-Method'는 실제 요청을 전송할 때 POST 메서드로 전송된다는 것이고, 'Access-Control-Request-Headers'는 실제 요청을 전송 할 때 X-PINGOTHER 와 Content-Type아 사용자 정의 헤더와 함께 전송된다는 것을 서버에 알려준다.
  • Preflight response
    서버는 메서드와 헤더를 받을 수 있음을 알려준다. 위의 Preflight response에서는 'Post'와 'GET'이 유용한 method라고 알려준다. 마지막행('Access-Control-Max-Age')은 preflight request에 대한 응답을 캐시할 수 있는 시간(초)이다.
    Preflight request를 보내면 사전, 실제 요청 두번이 매번 왔다갔다 하므로 브라우저가 캐싱을 해두고 똑같은 요청을 보낼때, 사전 요청을 보내지 않고 바로 본 요청을 보낸다.
    Preflight request가 완료되면 실제 요청을 전송한다.
    Preflight에서 중요한 것은 'Access-Control-Allow-Origin'인데 header 내의 이 값을 확인하여 Browser가 CORS 작동유무를 판단한다.

👉 Simple request

Simple request는 Preflight Request와 다르게 요청을 보내면서 즉시 cross origin인지 확인하는데, 다음 조건을 모두 충족해야한다.

  • 다음 중 하나의 method - GET, HEAD, POST
  • Accept, Accept-Language, Content-Language, Content-Type의 Header
  • Content-Header는 application/x-www-form-urlencoded, multipart/form-data, text/plain 만 허용

Simple request(단순 요청)는 예비 요청을 보내지 않고 바로 서버에게 본 요청부터 보낸 후 서버가 이에 대한 Response header에 Access-Control-Allow-Origin과 같은 값을 보내주면 그때 브라우저가 CORS 규칙 위반 여부를 검사하는 방식이다. 즉, 프리플라이트와 Simple request 시나리오는 전반적인 로직 자체는 같고 preflight request의 존재 유무만 다르다.

👉 Credentialed request

세번째 시나리오는 인증된 정보를 포함한 request이다. 다른 방식들 보다 좀 더 보안을 강화하고 싶은 경우 사용된다.
브라우저가 제공하는 비동기 리소스 요청 API인 XMLHttpRequest 객체나 fetch API는 별도의 옵션 없이 브라우저의 쿠키 정보나 인증과 관련된 헤더를 기본적으로 요청에 담지 않으므로, 이와 관련하여 따로 crudential option을 정해 주어야 한다.

  • 3가지 option
  1. same-origin (기본값) : 같은 출처 간 요청에만 인증 정보를 담을 수 있다
  2. include : 모든 요청에 인증 정보를 담을 수 있다
  3. omit : 모든 요청에 인증 정보를 담지 않는다

리소스 요청에 인증 정보를 담는 옵션을 사용한다면, 브라우저는 다른 출처의 리소스를 요청할 때 단순히 Access-Control-Allow-Origin만 확인하는 것이 아닌 추가로 검사를 하게 된다.

요청에 인증 정보가 담겨있는 경우, 다른 출처의 리소스를 요청하게 되면 브라우저는 CORS 규칙 위반 여부를 검사하는 룰에 다음 두 가지를 추가하게 된다.
1. Access-Control-Allow-Origin에는 *를 사용할 수 없으며, 명시적인 URL이어야함
2. 응답 헤더에는 반드시 Access-Control-Allow-Credentials: true가 존재해야함

📖 출처 :
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
https://evan-moon.github.io/2020/05/21/about-cors/

My opinion

이번 posting을 통해 왜 초기 세팅 시 'django-cors-headers'를 설치해야 되는지와 cors의 중요성을 알게 되었다. 알면 알수록 더 알고 싶은 세계인 것 같다.

profile
운동하는 개발자

0개의 댓글