-
개발자들을 괴롭히는 SOP(동일 출처 정책)와 CORS(교차 출처 리소스 공유)Web 2021. 7. 21. 02:26
CORS 에러는 프론트엔드-백엔드 간 협업을 곤란하게 하는 악의 축이다
나도 관통프로젝트 할 때 CORS 에러에 의해 앓아누울 뻔 한 적이 있었다가 구글링을 통해 해결했는데, 생각해보니 CORS 에러가 무엇인지 어떤 의미인지에 대해서는 몰랐었기에
https://www.youtube.com/watch?v=bW31xiNB8Nc
https://www.youtube.com/watch?v=-2TgkKYmJt4
위의 두 영상을 보고 CORS, 그리고 CORS 에러의 원인인 SOP 정책에 대해 공부하게 되었다.
SOP(Same-Origin Policy)란 어떤 출처(프로토콜, 호스트, 포트의 조합을 한 출처라고 한다)에서 불러온 문서, 스크립트, 리소스가 다른 출처에서 가져온 리소스와 상호작용하는 것을 제한하는 보안 방식이다
이러한 정책은 잠재적으로 해로울 수 있는 문서를 분리하여 공격받을 수 있는 경로는 줄이는 것을 의미한다(MDN)
https://ko.wikipedia.org/wiki/%EB%8F%99%EC%9D%BC-%EC%B6%9C%EC%B2%98_%EC%A0%95%EC%B1%85 이러한 식으로 프로토콜, 호스트, 포트 중 하나라도 다르면 브라우저는 같은 출처라고 보지 않는다.
다른 출처에 대한 접근 제한을 막지 않을 시 아래와 같은 상황이 발생할 수 있다
1️⃣ 사용자가 웹 사이트에 로그인을 시도한다
2️⃣ 로그인 성공과 함께 인증 토큰을 발급받아 쿠키에 저장한다
3️⃣ 외부에서 사용자에게서 인증 토큰의 탈취를 시도한다(ex. 이메일 클릭 시 script 실행)
4️⃣ 사용자의 인증 토큰을 뺏어온다
5️⃣ 웹 사이트를 통해 인증 토큰을 가진 다른 출처로 접근하여 서비스를 이용할 수 있다(해킹 위험)
이 때, 동일한 인증 토큰으로 접근하더라도 사용자의 출처(ex. 192.173.0.30:8080 vs http://unknown.com:8092)는 다른 른 출처를 가지고 있기에, 다른 출처로의 접근을 제한하는 것이 SOP 보안 방식이다.
하지만 개발을 하다보면 다른 출처로서의 리소스를 허용해야 하는 순간이 반드시 있기 때문에 CORS를 사용한다.
CORS(Cross-Origin Resource Sharing)은 추가 HTTP 헤더를 사용하여, 한 출처에서 실행중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다.
CORS를 허용하면 웹 페이지는 교차 출처 이미지, 스타일시트, 스크립트, 동영상 등의 자원들을 임베드하여 사용할 수 있다.
https://mdn.mozillademos.org/files/14295/CORS_principle.png SOP에 의해 Ajax 요청이 기본적으로 금지되기 때문에 Ajax를 사용할 때 CORS 에러를 자주 접할 수 있다.
CORS가 동작하는 방식에는 크게 세 가지가 있지만 여기에서는 두 가지 방식만 간단하게 살펴보기로 한다.
1. Simple Requests
다음의 요청들을 모두 성립해야 한다
1️⃣ 특정 HTTP Method(GET/HEAD/POST)를 사용
2️⃣ 자동으로 설정된 헤더 이외에는 다음의 헤더만 사용 가능
- Accept
- Accept-Language
- Content-Launguage
- Content-Type
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
3️⃣ Content-Type 헤더는 다음의 값들만 허용
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
(이외에도 두 가지 조건이 더 있지만 생략한다)
GET /resources/public-data/ HTTP/1.1 Host: bar.other User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0 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 Connection: keep-alive Origin: https://foo.example
브라우저가 위와 같이(헤더의 Origin을 명시) 서버로 요청을 보내면
HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 00:23:53 GMT Server: Apache/2 Access-Control-Allow-Origin: * Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: application/xml […XML Data…]
서버는 Access-Control-Allow-Origin 헤더를 사용하여 응답한다.
이 때에는, 모든 도메인에서 접근할 수 있음을 의미하며 특정 출처만 허용하려는 경우에는 Access-Control-Allow-Origin에 해당 도메인을 명시하여 보낸다.
2. Preflighted Request
simple request와는 달리, OPTIONS 메소드를 통해 다른 도메인의 리소스로 HTTP 요청을 보냈을 때 전송이 안전한지 확인하는 방식이다.
위처럼 Preflight에서의 전송이 안전하게 확인되면 메인 요청을 전달하는 방식으로 사용한다.
OPTIONS /resources/post-here/ HTTP/1.1 Host: bar.other User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0 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 Connection: keep-alive Origin: http://foo.example Access-Control-Request-Method: POST Access-Control-Request-Headers: X-PINGOTHER, Content-Type HTTP/1.1 204 No Content Date: Mon, 01 Dec 2008 01:15:39 GMT Server: Apache/2 Access-Control-Allow-Origin: https://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 Keep-Alive: timeout=2, max=100 Connection: Keep-Alive
위와 같이 preflight request가 완료되면, 실제 요청을 전송한다.
POST /resources/post-here/ HTTP/1.1 Host: bar.other User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0 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 Connection: keep-alive X-PINGOTHER: pingpong Content-Type: text/xml; charset=UTF-8 Referer: https://foo.example/examples/preflightInvocation.html Content-Length: 55 Origin: https://foo.example Pragma: no-cache Cache-Control: no-cache <person><name>Arun</name></person> HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 01:15:40 GMT Server: Apache/2 Access-Control-Allow-Origin: https://foo.example Vary: Accept-Encoding, Origin Content-Encoding: gzip Content-Length: 235 Keep-Alive: timeout=2, max=99 Connection: Keep-Alive Content-Type: text/plain [Some GZIP'd payload]
3. Request with Credentials
이후 추가 예정
앞으로 시작할 공통PJT에서도 CORS 에러때문에 고통받는 일이 없었으면 좋겠다 ㅠ
참고 자료
https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy
https://developer.mozilla.org/ko/docs/Web/HTTP/CORS
https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
https://ko.wikipedia.org/wiki/%EB%8F%99%EC%9D%BC-%EC%B6%9C%EC%B2%98_%EC%A0%95%EC%B1%85'Web' 카테고리의 다른 글
GET Method는 URL을 256자 이하로 제한하지 않는다 (402) 2021.09.18 자주 사용하는 HTTP Status Code (1) 2021.07.27 WebRTC - 웹 브라우저 간 실시간 미디어 통신 기술 (0) 2021.07.10 로직을 UI로부터 분리하는 MVVM Architecture Pattern (0) 2021.06.24 채팅 시스템 구현을 위한 WebSocket 과 STOMP 프로토콜 (2) 2021.05.28