프로젝트 과제를 하면서 끊임없이 발견한 오류 중에 하나가 CORS 관련 오류였다.
Access to XMLHttpRequest at '주소A' from origin '주소B' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Django랑 React를 연동하면서 다음과 같은 오류 메세지를 계속 접할 수 있었는데 로그인을 할 수 없어 서평 공간 등의 ui를 확인하기 어려웠다. 서평 공간 컴포넌트 관련하여 코드 수정할 때는 서평 공간 등의 컴포넌트를 publicRoute 상태로 바꿔주고 작업을 해왔었다. 일단 서평 작성과 공간 부분 기능 구현을 한 후 이번 기회에 자세히 cors에 대해, 그리고 그 원인을 알아보고자 한다.
먼저, CORS를 이해하기 위해서는 origin을 이해해야 하는데,
Origin은 간단하게 프로토콜, 주소, 포트번호의 쌍을 말한다.
Origin => [프로토콜(Sceme)]://[Host의 IP 주소 또는 URL]:[포트번호]
여기서 포트 번호는 생략 가능하다. 기본적으로 HTTP 방식으로 오면 80, HTTPS 방식으로 오면 443을 이용하게 된다.
다른 Origin이라고 하면 프로토콜(HTTP, HTTPS)이 다르거나 주소(a.com, b, com,...)가 다르거나 포트번호(80, 433,...)가 다르다는 말이다.
mozilla 개발자 페이지에 따르면, Same Origin Policy(SOP)란 한 origin으로부터 로드된 document 또는 script가 다른 origin 리소스와 상호작용을 할 수 있는 방법을 제한 하는 중요한 보안 메커니즘을 말한다.
더 쉽게 얘기하자면, 내가 접속한 사이트(Origin)에서 다른 Origin에 요청한 것을 기본적으로 제한해서 어느정도 해커의 공격에 방어하는 것이다.
이 SOP 정책으로 인해 우리는 다음과 같은 document 등에 접근이 불가능해진다.
- document
- </img/>, </script/>, </link/>, </iframe/>과 같은 특정 HTML Tag
=> 다른 Origin으로부터 온 것은 임베딩할 수 있게 허용해주지만, 데이터를 읽는 건 보안상의 이유로 차단한다.- origin마다 있는 웹 데이터베이스인 localStorage, sessionStorage
무엇으로 요청을 하느냐에 따라 홈페이지 내부로의 데이터 로드 여부가 달라지는데, iframe 을 요청한 경우에는 origin 내부로 로드는 되지만, 실제 데이터는 볼 수 없다. 하지만 XMLHttpRequest를 이용하게 되면 데이터가 로드되기 전에 origin 검사를 해서 same-origin일 경우 로드와 데이터를 불러오지만, cross-origin일 경우엔 로드 조차 될 수 없게 막는다.
또한 보통은 다른 origin으로 데이터 write는 할 수 있는데, preflight방식을 사용하면 이 또한 불가능하다. Preflight에 대해서는 다음에 알아보는 것으로,,
간단하게 말하면, Preflight Request는
다른 Origin 요청을 보낼 때 미리 내 요청을 받을 수 있는지 확인하기 위해서 사전 요청(Preflight Request)을 보내게 된다. 그러고 나서 가능하면 나의 실제 요청을 보내고 응답을 받는다.
드디어 CORS이다. 우리는 종종 다른 Origin으로부터의 자원을 불러오고 싶은 경우가 있다. 이렇게 cross-origin간의 통신이 필요한 경우를 대비해 CORS(Cross Origin Resource Sharing) 정책이 생겼다. 그러니까 SOP이 cross-origin간에 데이터 통신을 막기 위한 것이면, CORS는 제한적으로 허용하게 하기 위한 것이라고 생각할 수 있겠다.
간단하게 말하면, CORS는 허용한 origin만을 Access-Controll-Allow-Origin 에 추가함으로써 사용할 수 있다.
JSONP라는 방식도 있긴한데 이건 모든 origin을 대상으로 SOP을 무력화 시키기 때문에 해킹과 같은 보안 문제에 매우매우 취약하다. 그러므로 사용하지 말 것!
Django 파일에서 django-cors-headers를 설치해준다.
pip install django-cors-headers
settings.py를 변경해준다.
ALLOWED_HOSTS = ['localhost', '127.0.0.1', '127.0.0.1:3000', '127.0.0.1:8000']
INSTALLED_APPS = [
...
'corsheaders',
]
MIDDLEWARE = [
...
'corsheaders.middleware.CorsMiddleware',
]
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_WHITELIST = [
'http://127.0.0.1:8000',
'http://127.0.0.1:3000',
]
이렇게 했더니 깔끔하게 Django와 React이 연동되었다.