일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- springboot
- ReactNative
- web view
- Project Bee
- 자료구조
- service 테스트
- Navigation
- bfs dfs
- 해외 대외활동
- react
- 버튼 활성화
- 휴대폰 기기
- 창의충전소
- 백준 1992
- multipart upload
- 완전탐색
- 폴더구조
- 이영직
- React Natvive
- React Native
- FlatList
- BFS
- 티스토리챌린지
- 경우의 수
- 원복
- 오블완
- 구현
- 비트마스킹
- 노마드코더
- 상속 관계 매핑
- Today
- Total
유미의 기록들
[네트워크 -1] CORS란? 본문
토이 프로젝트를 하면서 팀원이 로컬환경에서 개발한 프론트엔드 어플리케이션과, 내가 만들었던 API 서버와 통신을 시도했는데 CORS 관련 이슈가 발생했다
웹 개발자라면 한번 쯤은 마주치는 에러인데, CORS 정책 위반으로 인해 에러가 발생하는 것이다
CORS (Cross-Origin Resource Sharing)
한국어로 직역하면 "교차 출처 리소스 공유"하고 해석할 수 있다. 말만 보고는 어떤 의미인지 짐작하기 어려운 데 여기서 교차 출처라는 것을 다른 출처를 의미한다고 보면 된다
먼저 출처(Origin)는 프로토콜, Host, 포트 번호까지 모두 합친 것을 의미한다
출처 내의 포트번호는 생략이 가능한데, 웹에서 사용하는 HTTP, HTTPs 프로토콜의 기본 포트 번호가 정해져 있기 때문이다. HTTP가 정의된 RFC2616 문서를 보면 기본 포트가 80번으로 정의되어 있는 것을 볼 수 있다.
그런데 만약 포트번호가 명시적으로 포함되어 있다면 포트번호까지 일치해야 한다
즉, 출처를 구성하는 세 요소 중 하나라도 다르면 다른 출처라고 볼 수 있다. '출처가 교차한다'라는 건 '두 출처가 서로 다르다'라는 뜻으로, CORS를 설정한다는 건 서로 다른 출처라도 리소스 요청, 응답을 허용할 있도록 하는 정책이다. 반대로 SOP는 서로 다른 출처일 때 리소스 요청과 응답을 차단하는 정책이다.
이렇게 다른 출처로의 리소스 요청을 제한하는 것과 관련된 2가지 정책인 Same Origin 정책과 Cross Origin 정책에 대해서 자세히 알아보도록 하자
동일 출처 정책 (Same-Origin Policy)
SOP는 지난 2011년, RFC 6454에서 처음 등장한 보안 정책으로 단어 그대로 동일한 출처에서만 리소스를 공유할 수 있다. 즉, 다른 출처 서버에 있는 이미지나 영상 같은 리소스는 상호작용이 불가능 하다는 것이다
이전에는 프론트엔드와 백엔드를 따로 구성하지 않았기 때문에 모든 처리가 같은 도메인 안에서 가능했다. 그래서 다른 출처로 요청을 보내는게 의심스러운 행위로 보였다. 그런데 웹이 발달하면서 클라이언트에서 API를 직접 호출하는 방식이 많아졌다
RFC 6545문서를 보면, 웹 환경에서는 다른 출처에 있는 리소스를 가져와서 사용하는 일이 흔한 일이라 무조건 막을 수 없기 때문에 몇가지 예외 조항을 두고 이에 해당하는 리소스 요처은 출처가 다르더라도 허용하기로 했는데 그 중 하나가 CORS 정책이다
다시 정리를 해보자면, 다른 출처로 리소스를 요청하면 SOP 정책을 위반한 것이고 SOP의 예외 조항인 CORS 정책까지 지키지 않을 시, 아예 다른 출처의 리소스를 사용할 수 없게 되는 것이다
어차피 개발자는 정해진 서버하고만 통신을 하도록 코드를 작성할 텐데 왜 이렇게 귀찮은 정책을 지켜야 할까?
동일 출처 정책이 필요한 이유
출처가 다른 두 어플리케이션이 자유롭게 통신할 수 있는 환경은 꽤 위험하다. 특히, 웹에서 돌아가는 클라이언트 어플리케이션은 사용자 공격에 너무 취약하다. 당장 개발자 도구만 열어도 어떤 서버와 통신하는 지, 리소스 출처는 무엇인 지 열람할 수 있기 때문이다.
해커가 CSRF (Cross-Site Request Forgery)나 XSS(Cross-Site Scripting)와 같은 방법을 사용하여 우리의 어플리케이션에서 코드가 실행된 것처럼 꾸며서 개인 정보를 가로챌 수 있다
그렇다면 어떻게 CORS 정책을 따르게 하여 SOP 정책을 회피할 수 있을까?
여기서 중요한 사실은 이렇게 출처를 비교하는 로직이 서버가 아닌 브라우저에 구현되어 있는 스펙이라는 것이다.
즉, 브라우저를 통하지 않고 서버 간 통신을 할때는 이 정책이 적용되지 않는다. 그래서 이를 이용한 프록시 서버라는 것이 있다.
CORS 에러 대응하기
1. 클라이언트에서 HTTP 요청의 헤더에 Origin을 담아 전달
기본적으로 웹 클라이언트 어플리케이션이 다른 출처의 리소스를 요청할 때는 HTTP 프로토콜을 사용하여 요청을 보내는데, 이때 브라우저는 요청 헤더에 `Origin`이라는 필드에 요청을 보내는 출처를 함께 담아보낸다.
2. 서버에서 Access-Control-Allow-Origin 헤더를 설정
요청을 수락 할 출처를 `Access-Control-Allow-Origin`에 명시적으로 지정하면 출처가 다르더라도 명시한 리소스 요처을 허용하게 된다.
3. 브라우저는 자신이 보냈던 Origin과 Access-Control-Allow-Origin을 비교
응답을 받은 브라우저는 자신이 보냈던 요청의 `Origin`과 서버가 보내준 응답의 `Access-Control-Allow-Origin`을 비교해본 후, 이 응답이 유효한 지 결정한다
결국 내가 해결해야 할 문제의 해결책은 서버에서 `Access-Control-Allow-Origin` 헤더에 허용할 출처를 기재해서 클라이언트에 응답하면 되는 것이였다.
기본적인 흐름은 이렇게 간단하지만, 사실 CORS가 동작하는 방식은 세 가지의 시나리오에 따라 변경되기 때문에 우리의 요청이 어떤 시나리오에 해당되는 지 잘 파악하면 CORS 정책 위반으로 인한 에러를 고치는 것이 쉬울 것이다.
CORS 동작방식
1. 예비 요청 (Preflight Request)
일반적으로 우리가 웹 어플리케이션을 개발할 때 가장 많이 마주치는 시나리오이다.
사실 브라우저는 요청을 보낼 때 한번에 바로 보내지 않고, 먼저 예비 요청(Preflight)을 보내 서버와 잘 통신되는 지 확인한 후 본 요청을 보낸다
(이때 예비 요청의 HTTP 메소드는 OPTIONS이 사용된다는 것이 특징이다)
1) JavaScript의 `fetch()` 메서드를 통해 리소스를 받아오라는 요청을 보낸다
2) 브라우저는 서버로 OPTIONS 메소드로 예비 요청을 보낸다
3) 서버는 예비 요청에 대한 응답으로 어떤 것을 허용하고 금지하는 지 헤더 정보에 담아서 브라우저로 보낸다
4) 이후 브라우저는 보낸 요청과 서버가 응답해준 정책을 비교하여, 해당 요청이 안전한 지 확인하고 본 요청을 보낸다
5) 서버가 본 요청에 대한 응답을 하면 최종적으로 응답 데이터를 Javascript로 넘겨준다
요청을 보내기 전에 예비 요청을 보내 보안을 강화하지만, 결국에는 실제 요청에 걸리는 시간이 늘어나게 되면서 어플리케이션 성능이 안좋아지는 단점이 있다
따라서 브라우저 캐시(Cache)를 이용해 `Access-Control-Max-Age` 헤더에 캐시될 시간을 명시해 주면, 이 Preflight 요청을 캐싱 시켜 최적화를 시켜줄 수 있다.