개요
- 드디어(?) 맞닥뜨리게 된 CORS 에러에 대해 알아보자
1. Same-Origin 정책 & Cross-Origin 정책
- HTML문서 : 기본적으로 Cross-Origin 정책을 따름
- script 태그, XMLHttpRequest, Fetch API 등 : 기본적으로 Same-Origin 정책을 따름
- 자바스크립트는 기본적으로 서로 다른 도메인에 대한 요청을 보안상 제한한다
- 자바스크립트 -> 브라우저에서 실행되는 언어
- 브라우저는 기본적으로 하나의 서버 연결만 허용되도록 설정되어 있기 때문 (주로 자신의 서버)
- jQuery같은 외부 자바스크립트 소스를 가져와 로드할 수 있는 이유는, CDN 서비스 서버에서 모든 출처를 허용하도록 되어있기 때문
- 자바스크립트는 기본적으로 서로 다른 도메인에 대한 요청을 보안상 제한한다
1-1. Origin(출처)란?
어떤 사이트를 접속할 때, 인터넷 주소창에 URL 이라는 문자열을 통해 접근하게 된다.
https://www.domain.com:3000/user?query=name&page=1#first
Protocol
: http, https (https://
)Host
: 사이트 도메인 (www.domain.com
)Port
: 포트 번호 (:3000
)Path
: 사이트 내부 경로 (/user
)Query string
: 요청의 key와 value값 (?query=name&page=1
)Fragement
: 해시 태그 (#first
)
Origin
: Protocol + Host + Port
즉, 출처(Origin)는 Protocol과 Host, Port를 모두 합친 URL을 의미한다.
1-2. 동일 출처 정책(Same-Origin Policy)
CORS 에러 → 브라우저의 동일 출처 정책(Same-Origin Policy)을 위반하면 발생하는 에러 인데, 여기서 동일 출처란 프로토콜, 호스트(도메인), 포트가 모두 같은 것을 말한다.
예를 들어, http://mypage:8080
에서 요청을 보낸다 했을 때 동일 출처 정책을 위반한 사례는 다음과 같다
https://mypage:8080/~
→ 프로토콜 불일치http://yourpage:8080/~
→ 호스트(도메인) 불일치http://mypage:9080
~ → 포트 불일치
이는 보안상의 이유로 생겨난 정책이다. (CSRF, Cross-site Request Forgery), (XSS, Cross-site Scripting) 등의 공격에 대비한 것
1-3. 교차 출처 리소스 공유(Cross Origin Resource Sharing)
- 원칙적으로 동일 출처 정책에 따르면, 서로 다른 출처 간에는 리소스 공유가 불가능해야 한다.
- (정책의 이유는 보안 때문)
- 하지만, 서로 다른 출처라도 정해진 규칙에 맞다면 자원 공유를 허락해준다.
- 서로 다른 출처 간의 자원 공유. 이를 위한 규칙을 교차 출처 리소스 공유라 부른다.
- 우리가 가져오는 리소스들이 안전한지 검사하는 브라우저의 방화벽이라고 볼 수 있음!
즉, 추가 HTTP 헤더를 사용하여 한 출처에서 실행 중인 웹 어플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제
2. 브라우저의 CORS 기본 동작 살펴보기
- 클라이언트에서 HTTP 요청의 헤더에 Origin을 담아 전달
- 기본적으로 웹은 HTTP 프로토콜을 이용하여 서버에 요청을 보내게 되는데, 이때 브라우저는 요청 헤더에 Origin이라는 필드에 출처를 함께 담아 보내게 된다.
- 서버는 응답헤더에
Access-Control-Allow-Origin
을 담아 클라이언트로 전달- 서버가 클라이언트의 요청에 대한 응답을 할 때 응답 헤더에
Acess-Control-Allow-Origin
이라는 필드를 추가하고 값으로 이 리소스를 접근하는 것이 허용된 출처를 내려보낸다.
- 서버가 클라이언트의 요청에 대한 응답을 할 때 응답 헤더에
- 클라이언트에서 자신이 보냈던 요청의 Origin과 서버가 보내준
Access-Control-Allow-Origin
을 비교- 응답을 받은 브라우저는 자신이 보낸 Origin과 서버가 보낸 Access-Control-Allow-Origin 을 비교하여 차단할지 말지 결정한다
2-1. CORS 해결책은 서버의 허용이 필요
- 위 CORS 동작 과정에서 알 수 있었던 것은 바로 서버에서
Access-Control-Allow-Origin
헤더에 허용할 출처를 기재해 클라이언트에 응답해주면 CORS 오류가 해결된다는 사실이다!
-- client의 요청헤더
Origin : http://localhost:1004
...
-- Server의 응답헤더
Access-Control-Allow-Origin : http://localhost:1004
- 이렇게 요청 출처와 응답의 허용 출처가 같으면 CORS 정책을 위반하지 않은게 된다!
2-2. 출처 비교와 차단은 브라우저가 한다
- 요청을 했을 때 오류가 나면 서버의 문제라고 생각할 수 있지만, 엄연히 말하면 출처를 비교하는 로직은 서버에 구현된 스펙이 아닌, 브라우저에 구현된 스펙이다.
- 즉, Browser 내에서 웹 어플리케이션의 Javascript가 요청을 보냈을 때 서버에서는 해당 요청을 정상적으로 받고, 응답 데이터도 정상적으로 보냈지만, 브라우저 단에서 이 출처가 다르면 응답 데이터를 받을 수 없도록 차단하는 것이다
- 브라우저가 정책으로 차단을 한다는 말은, 브라우저를 통하지 않고 서버 간 통신을 할때는 정책이 적용되지 않는다는 말과도 같다
- 즉, 클라이언트 단 코드에서 API를 요청하는게 아니라, 서버 단 코드에서 다른 출처의 서버로 API 요청을 하면 CORS 에러로부터 자유로워진다
- 그래서 이를 이용한
프록시(Proxy) 서버
라는 것도 있다
- 그래서 이를 이용한
3. CORS 해결방법
3-1. 남이 만든 프록시 서버 사용
클라이언트에서 외부 서버로 바로 요청이 아니라, 프록시 서버를 사용해 한번 우회하는 방법.
이 경우 프록시 서버를 통해 다른 네트워크 서비스에 간접적으로 접속할 수 있게 되며, HTTP 요청 헤더에 Access-Control-Allow-Origin:*
를 설정하여 응답하게 할 수 있다.
요청해야 하는 URL 앞에 프록시 서버 URL을 붙여서 요청하게 되면, 간단하게 문제를 해결할 수 있다.
3-2. 직접 프록시 서버 구축
직접 서버를 구축하게 되면 더이상 클라이언트에서 서버로 요청하는 것이 아니라, 서버에서 서버로 요청할 수 있게 된다.
이 때, CORS는 브라우저에 관련된 정책이기 때문에, 브러우저를 통하지 않고 서버 간 통신을 할때는 이 정책이 적용되지 않는다.
즉, 서버에서 서버로 리소스를 요청하면 CORS 정책을 위반하지 않고 정상적으로 응답을 받을 수 있다.
3-3. 서버 : Access-Control-Allow-Origin
클라이언트에서 해결하는 것보다는 서버에서 헤더를 세팅해주는 것이 가장 기본적인 CORS 해결 방법이다.
서버를 따로 구축하지 않고, 외부 서버에 리소스를 요청해서 헤더를 세팅할 수 없는 경우에는 사용할 수 없는 방법이지만(혹은 프록시 서버를 사용하거나) 클라이언트와 서버 모두 자신이 제어할 수 있다면 서버에서 Access-Control-Allow-Origin 헤더를 세팅해주는 것이 가장 좋다.
참고사이트
'Web' 카테고리의 다른 글
[debug] 디버깅 팁 정리 (feat. 웹 프로젝트) (0) | 2023.11.16 |
---|---|
[Web] web.xml 설정파일 (0) | 2023.10.19 |