SOP와 CORS란
SOP(Same Origin Policy, 동일 출처 정책)란
다른 출처의 리소스를 사용하는 것을 제한하는 보안 방식이다. 다시말해 출처가 동일해야 리소스에 대한 상호작용이 가능하다
예를 들어보자. https://example1.com에서 https://example2.com에게 필요한 리소스를 요청할때 example2에서 요청을 한 곳의 출처를 확인한다.
SOP에 위반하기 때문에 요청을 반환한다.
출처란
출처는 URL의 Protocol, Host, Port를 통해 같은 출처인지 다른 출처인지 판단한다.
url 구조
출처를 판단하기 위해서는 url를 살펴봐야 한다. 3가지를 보고 출처를 판단하게 되는데 1. protocol
, 2. host(도메인)
, 3. port(생략가능)
이렇게 3가지를 보고 판단한다.
3개중에 하나라도 다르다면 SOP에 위반하여 요청이 반환된다. 그렇다면 위에서는 host 즉 도메인이 달라 다른 출처로 판단이 내려져 요청이 반환된 것을 알것이다.
(IE는 SOP에 포트를 포함하지 않기 때문에 포트가 달라도 같은 출처로 인식될 수 있다)
CORS(Cross Origin Resource Sharing, 교차 출처 리소스 공유)란
그렇다면 다른 출처의 리소스가 필요하다면 어떻게 해야할까 ? 교차 출처의 접근을 허용하는 CORS를 사용해야 한다. CORS는 어떤 출처의 리소스에 다른 출처가 접근할 수 있도록 한다. 즉 https://example1.com에서 https://example2.com에게 필요한 리소스를 요청하여 요청을 받을 수 있게 해준다.
CORS 동작 원리
- 브라우저가 리소스를 서버에 요청할때 요청 헤더에 정보(자신의 출처, 요청할 메소드)를 담아 전송한다.
- 서버에서는 응답 헤더에 Access-Control-Allow-Origin 이라는 값을 담아서 응답한다.
- 브라우저에서는 서버가 보낸 헤더를 보고 자신이 보낸 요청의 출처와 서버가 보낸 응답의 Access-Control-Allow-Origin 을 비교한 후 요청이 가능하면 리소스 전송을 허용하고 아니라면 에러를 발생시킨다.
cors의 동작 방식은 단순요청 방식과 예비요청 방식 2가지가 있다.
simple request (단순 요청)
단순 요청은 서버에 API를 요청하고 서버는 Access-Control-Allow-Origin을 헤더에 포함하여 응답한다. 브라우저는 Access-Control-Allow-Origin 헤더를 확인해서 CORS 동작을 수행할지 판단한다
서버에게 바로 요청을 보내는 방법으로 3가지의 조건이 모두 만족되는 경우 사전 요청없이 요청을 보낼 수 있다
- GET/HEAD/POST 중 한가지 메소드를 사용해야 한다
- Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width를 제외한 헤더를 사용하면 안된다
- application/x-www-form-urlencoded, multipart/form-data, text/plain 중에서만 Content type을 지정해야 한다
(대부분 3가지를 전부 지킬 수 없다. API로 데이터를 전송시 주로 application/json을 사용한다)
preflight request(예비 요청)
예비요청은 서버에 예비요청을 보내서 안전한지 판단 한 후 요청을 보낸다. 실제 리소스를 요청하기 전에 OPTIONS 라는 메소드를 통해 실제 요청을 전송할지를 판단한다
서버에서는 이 요청에 대한 응답으로 Access-Control-Allow-Origin 헤더를 포함한 응답을 브라우저에 보낸다.
브라우저에서는 Access-Control-Allow-Origin 헤더를 확인하여 CORS 동작을 수행할지를 판단한다
이때 서버등의 헤더에 리소스에 대한 접근을 허락하는 내용이 없으면 cors 문제가 발생한다.
django에서 cors 해결
장고에서는 관련 패키지를 설치해 보다 쉽게 cors를 설정할 수 있다
- 우선 패키지를 설치한다
cors에 필요한 서버의 헤더를 조작하기 위한 패키지이다
pip install django-cors-headers
# settings.py
INSTALLED_APPS = [
..
..,
'corsheaders',
]
- settings에서 cors관련 설정을 추가한다
서버의 응답에 cors 헤더를 추가하기 위해서 corsheaders를 installed apps목록에 추가한다.
middlewqre class도 추가한다. 이때 가장 높게 위치시켜야 한다
# settings.py
INSTALLED_APPS = [
.
.
'corsheaders',
]
MIDDLEWARE = [
.
.
'corsheaders.middleware.CorsMiddleware',
]
- settings에서 CORS_ORIGIN_WHITELIST는 cross site요청을 허용하는 호스트들을 직접 추가할 수 있다
모든 호스트를 허용하고 싶다면 CORS_ORIGIN_ALLOW_ALL을 True로 설정하면 된다. 이렇게 된다면 모든 출처에서의 요청이 허용된다
CORS_ORIGIN_ALLOW_ALL = True
or
CORS_ORIGIN_WHITELIST = (
허용하려는 HOST
)
Q. Access to fetch at 'http://127.0.0.1:8000/' from origin 'http://127.0.0.1:5501' has been blocked by CORS policy: Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response.
에러가 발생하는 이유는 ?
A. 장고에 세팅즈 CORS_ALLOW_HEADERS에 request header에 담긴 데이터의 값을 똑같이 넣어주어야 한다. 나는 이거때문에 하루를 까먹었다…. 에러 메시지를 읽어보면 충분히 예측할 수 있는 문제였는데… 영어 공부 해야겠다 진짜…
Q. ajax로 쿼리스트링으로 데이터를 담아 get요청을 보낼때 위의 방식대로 설정을 했지만 cors관련 에러가 발생했었다. 아래 2개의 url 차이가 뭐길래 cors에러가 나는걸까..
http://127.0.0.1:8000/post?id=3 # cors 관련 에러 발생
http://127.0.0.1:8000/post?id=3& # 정상 작동