프로젝트를 위해 django 초기 세팅을 할 때, cors-headers를 추가하게 된다. cors가 무엇인지, 그리고 same origin policy가 무엇인지 정리했다.
django 초기 셋팅 때, 아래와 같이 CORS 설정해야 한다.
##CORS
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',
)
CORS는 'cross origin resource sharing'의 약자다. 웹 상 존재하는 여러 policy 중 하나. (위키백과에서는 '교차 출처 리소스 공유'라고...)
일단 origin 의 뜻은, '출처'다. 웹 상에서 정의하는 출처는:
접근할 때 사용하는 URL의 scheme(프로토콜), 호스트(도메인), 포트로 정의한다. 만약 2개의 url이 있고 위 3가지 요소가 모두 일치하는 경우 같은 출처를 가졌다고 말한다. (출처: MDN)
아래와 같이 2개 url은 같은 출처를 갖는다.
1) https://example.com
> http의 기본 port는 80으로, 생략 가능
2) https://example.com:80
아래의 2개는 다른 출처를 갖는다.
1) https://example.com
> port: 80
2) https://example.com:8080
> port: 8080
아래의 2개도 역시 다른 출처다. (다른 프로토콜)
1) http://example.com
2) https://example.com
origin 제한과 관련된 정책이 2가지가 있는데,
그것이 SOP (same-origin policy)와 CORS(Cross origin resource sharing)이다.
SOP는 같은 출처 상에서만 리소스를 공유할 수 있다는 정책이다. 이러한 정책이 필요한 이유는, 웹 어플리케이션들은 기본적으로 외부 침입에 취약하기 때문. 만약 어떤 사용자가 관리자가 아님에도 불구하고 악성 스크립트를 삽입하고 어플리케이션이 제대로 검사하지 않은 채 사용한다면 주요 정보들이 새나갈 것이다. (XSS나 CSRF)
하지만 다른 출처라 해도 리소스를 공유할 수 있는 예외 상황이 있는데, 그 중 하나가 CORS를 준수했을 때이다.
그런데 origin 비교는 클라이언트가 진행한다.
그러니까 일단 서버에 요청하면, 서버는 CORS 등과 상관없이 request에 대한 response를 하고, 이것이 CORS를 준수했는지 아닌지는 웹 브라우저가 판단한다. 그래서 CORS 에러가 떴건 말건, 서버 상에서는 response 완료 라는 로그만 남는다. 그래서 CORS를 이해하고 이를 적용하는 것이 중요하다.
CORS 요청이 가능하려면, reqeust header/response header에 관련 셋팅을 해야 한다.
예를 들어, domainx.com
에서 domainy.com
에게 리소스 요청을 한다고 가정하자.
그러면 domainx.com
의 요청 header에는 아래 내용이 포함된다.
Origin: http://domainx.com
domainy.com
은 아래와 같은 내용을 header에 포함해 응답한다.
Access-Control-Allow-Origin: http://domainx.com (혹은 *)
그러면 domainx.com
은 CORS정책에 따라 access가능한 origin 정보를 확인 후 리소스 접근 여부를 판단한다. 위의 경우, domainx.com
은 접근 가능하다고 셋팅되어 있으므로 리소스를 불러올 수 있다.
그런데 위와 같은 사례는 아주 단순하게 보여준 사례고, 실제로는 더 많은 정보를 주고 받으면서 CORS 위반 여부를 확인한다. 그리고 더 복잡한 요청-응답이 이뤄지는 대부분의 경우 preflight
(예비 요청)이 먼저 이뤄진다. 브라우저 스스로 이 요청이 안전한 요청인지 유효성을 검사하는 것이다. 이 경우에 해당하는 시나리오는 3가지:
GET
, POST
외 다른 메소드가 포함된 경우preflight
요청을 할 때는 아래 예시와 같이 request header가 작성된다.
(OPTIONS
라는 메소드 사용해서 요청을 보냄)
OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
response header는 아래와 같다.
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
In a nutshell...
CORS 관련 셋팅은 front-back 양측에게 중요한 일이다. 그래서 django 초기 셋팅 시, 허용하는 origin / method / header를 포함하는 것은 반드시 필요하다.
📌추가 용어
(220315 update) XSS를 정리하면서도 약간 공격 형태가 모호해서, 영상을 찾아봤는데 생활코딩에 좋은 영상이 있었다. 보면서 좀 더 이해하는데 도움이 되었음.
참고 자료
https://developer.mozilla.org/ko/docs/Glossary/Origin#다른_출처의_예제
https://www.keycdn.com/support/cors#http-response-headers
https://developer.mozilla.org/ko/docs/Web/HTTP/Methods/OPTIONS
https://evan-moon.github.io/2020/05/21/about-cors/
https://ko.wikipedia.org/wiki/MIME#Content-Type
https://4rgos.tistory.com/1