CORS

Nam Eun-Ji·2020년 11월 26일
0

HTTP

목록 보기
4/9

django에서 첫 초기환경세팅할 때 늘 가상환경에 기본적으로 django-cors-headers를 설치해주었다.
그래서 이 모듈이 대체 뭐하는 역할인가 찾다가 CORS에 대해 개념을 잡고 가고자 포스팅.



CORS란?

Cross-Origin Resource Sharing

  • 교차 출처 자원 공유
  • 브라우저 보안정책
  • HTTP 접근 제어
  • 브라우저간의 데이터를 주고받는 과정에서, 도메인 이름이 서로 다른 사이트간에 api요청을 할 때, 공유를 설정하지 않았다면 CORS에러가 발생한다.

요새는 프론트엔드와 백엔드 서버를 따로 구성하는 경우가 많다.
프론트엔드에서 다른 도메인이 위치한 API서버로 요청을 넣어야하는 상황이 생기는데, 이러한 기능은 당연히 지원되어야하는거 아닌가 생각할 수 있지만 예전에는 이 기능이 당연한 것이 아니었다.

CORS가 나오기 전

예전에는 웹브라우저 정책 중 하나가 도메인이 다르면 요청을 주고 받지 못하게끔 하는 것이었다.
유저가 웹브라우저에 주소를 치면 해당하는 서버로 요청을 보내게 되고 서버는 응답을 할 때 html페이지도 같이 반환하였다. 하나의 서버에서 비즈니스 로직과 html 페이지 빌드를 같이 하였었다. 즉 모든게 같은 도메인에서 일어났다는 것이다. 그 당시에는 웹사이트에서 다른 서버로 요청을 날린다는 것은 개인정보유출, 피싱사이트와 같이 보안상 악의적인 행동을 하는 것으로 여겨졌다. 그래서 웹브라우저에서는 같은 도메인이 아니면 요청 자체를 막는 선택을 하게 되었던 것이였다.


임시방편 - script 태그

하지만 점점 웹사이트에서 문서만을 제공하는 것이 아니라 어플리케이션을 만들기 시작하면서 기존 웹브라우저 보안 정책때문에 불편한 점이 조금씩 생겨나기 시작했다. 예를 들어 기존 웹사이트에 부가적인 기능으로 날씨를 보여주는 기능을 추가하고자 할 때, 기존 방법대로 하면 직접 구축한 웹서버에 날씨를 가져오는 라우터를 하나 추가하고 그것을 부르면 날씨 API서버에 다시 요청을 하고 날씨 정보를 받아와서 다시 웹사이트에 보여지게끔 반환한다. 그런데 여기서 궁금증! 굳이 웹서버를 거칠 필요없이 웹브라우저에서 바로 날씨 API 서버와 통신해서 날씨정보를 가져오면 되지 않을까?
그래서 개발자들은 JSONP 라는 방식을 이용했다.
HTML에서 script태그는 원래 스크립트를 불러오는 기능인데, 다른 도메인의 파일을 불러오는 것도 가능하다. 이를 이용하여 임시방편으로 다른 서버와 통신하게끔 한 것이다.


그 후 CORS

웹브라우저 입장에서는 우회적인 루트로 보안을 무력화시키는 것을 계속 방치할 수 없었지만, 그렇다고 이런 우회로를 버그로 판단하고 막기에는 너무 많은 수요가 있었다. 그래서 공식적인 루트를 열어준 것이 CORS.


CORS는 어떻게 동작하나?

크로스 도메인 요청을 보내는 작업은 사실 서버로 요청을 한번만 보내는 것이 아니다. 사용할 때 fetch API같은 것 딱 한번만 보내는 것처럼 보이지만 실제로는 두번보내는 것이다.

브라우저가 리소스를 요청할 때 추가적인 헤더에 정보를 담는다. 내 origin은 무엇이고 어떤 메소드를 사용해서 요청을 할 것이고 어떤 헤더들을 포함할 것인지를 담아서 서버에 전송한다. 서버는 서버가 응답할 수 있는 origin들을 헤더에 담아서 브라우저에게 보낸다. 브라우저가 이 헤더를 보고 해당 origin에서 요청할 수 있다면 리소스 전송을 허용하고 만약 불가능하다면 에러를 발생시킨다.

  • HTTP에서 origin은 프로토콜(scheme), 호스트(host), 포트(port)로 구성된다.
  • request헤더에는 Origin이, response헤더에는 Access-Control-Allow-Origin헤더가 있어야 한다.

서버에서도 웹브라우저로부터 크로스도메인 요청을 받으려면 동일한 라우터에 대해서 옵션메소드 처리를 따로 하도록 작업해주어야한다. 보통 서버라이브러리에서 간단히 해주기때문에 크게 신경쓸 필요가 없다. 이를 잘 활용하면 서버에서는 특정 라우터만 CORS 요청을 허용하게끔 하거나 혹은 특정 도메인에서 요청이 오게끔 허용해줄 수 있다.


요청 헤더 목록

Origin

Cross-site 요청을 날리는 요청 도메인 URI을 나타내며, access control이 적용되는 모든 요청에 Origin 헤더는 반드시 포함된다.

Access-Control-Request-Method

preflight요청을 할 때 실제 요청에서 어떤 메서드를 사용할 것인지 서버에게 알리기 위해 사용된다.

Access-Control-Request-Headers

preflight요청을 할 때 실제 요청에서 어떤 header를 사용할 것인지 서버에게 알리기 위해 사용된다.


응답 헤더 목록

Access-Control-Allow-Origin

Access-Control-Allow-Origin 헤더의 값으로 지정된 도메인으로부터의 요청만 서버의 리소스에 접근할 수 있게 한다.

  • origin에는 요청 도메인의 URI를 지정한다.
  • *은 credentials이 없는 요청에 한해서 모든 origin에서 접근이 가능하도록 허용한다.

Access-Control-Expose-Headers

브라우저가 액세스할 수있는 서버 화이트리스트 헤더를 허용한다.

Access-Control-Max-Age

preflight 요청 결과가 캐쉬에 얼마나 오랫동안 남아있는지를 나타낸다.

Access-Control-Allow-Credentials

Credentials가 true 일 때 요청에 대한 응답이 노출될 수 있는지를 나타낸다.

  • preflight요청에 대한 응답의 일부로 사용되는 경우 실제 자격 증명을 사용하여 실제 요청을 수행 할 수 있는지를 나타낸다.
  • 간단한 GET 요청은 preflight되지 않으므로 자격 증명이 있는 리소스를 요청하면 헤더가 리소스와 함께 반환되지 않으면 브라우저에서 응답을 무시하고 웹 콘텐츠로 반환하지 않는다.

Access-Control-Allow-Methods

preflight요청에 대한 대한 응답으로 허용되는 메서드들을 나타낸다.

Access-Control-Allow-Headers

preflight요청에 대한 대한 응답으로 실제 요청 시 사용할 수 있는 HTTP 헤더를 나타낸다.



Django REST API에 CORS 적용

# 가상환경내에서
pip install django-cors-headers
# settings.py
INSTALLED_APPS = [
    ...,
    'corsheaders'
]

MIDDLEWARE = [
    ...,
	'corsheaders.middleware.CorsMiddleware'

]
# 테스트환경에서나 아래 설정에서 모든 요청을 허용.
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_METHODS = (
    'DELETE',
    'GET',
    'OPTIONS',
    'PATCH',
    'POST',
    'PUT',
)
CORS_ALLOW_HEADERS = (
    'accept',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
)



참고
https://zzossig.io/posts/web/what_is_cors/
https://hannut91.github.io/blogs/infra/cors

profile
한 줄 소개가 자연스러워지는 그날까지

0개의 댓글