티스토리 뷰
CORS 정의
로그인, 회원가입 시 만나게 되는 CORS!
어떤 건지 정의를 정보통신용어 사전에 검색해봤는데, 개념이 난해하고 이해하기 쉽지 않다😮
CORS 는 웹 브라우저를 통해 서로 웹 서버에서 다른 도메인간 리소스 접근을 허용하도록 하는 웹 브라우저 기술 표준을 말한다. (출처: 정보통신용어사전)
아래처럼 일상적인 상황에 대입하면 좀 더 이해하기 쉬울 것 같아 정리해보았다.
1. A라는 사람이 있다고 가정해보자. A는 아주 바쁜 사업가다. 그래서 자신의 연락을 대신 관리해주는 사람인 B를 고용하게 된다.
2. A는 B에게 누군가 A의 사무실에 전화를 하면 통화를 바탕으로 발신자에 대한 정보(누구, 왜 등)를 수집하고 대기하라고 말해둔다.
3. B는 이후에 A에게 전화를 걸어 발신자에 대한 정보를 공유하고 A가 발신자와 대화 할 의향이 있는지 묻는다.
4. A는 앞으로 2시간 동안 abc 업무와 관련해서 작업 할 예정이라, abc 와 관련된 통화를 제외하고는 전화 연결 요청을 거부한다.
5. 그런데 만약 A의 개인번호를 아는 사람이 있다면 어떻게 될까? (ex. A의 친구) 이 사람은 굳이 B를 거쳐 통화하지 않고 A에게 곧바로 전화할 수 있다.
위 개념을 CORS 개념에 매핑해보자.
사무실에 전화하는 사람 -> 프론트엔드
B -> 브라우저
A -> 백엔드
프론트엔드와 백엔드간의 요청 처리 과정
흠..그렇다면 브라우저가 없는 경우에는 CORS 에러가 발생하지 않는 걸까?
이에 대한 해답은 프론트엔드와 백엔드간의 요청 처리 과정을 통해 알 수 있다.
1. 프론트엔드가 백엔드 API에 요청을 시도함
프론트엔드 앱이 특정 데이터를 얻거나 서버에 무언가를 전송하기 위해 백엔드 API에 요청을 보낸다.
2. 브라우저가 사전 요청(Pre-flight Request)을 보냄
브라우저는 CORS(교차 출처 리소스 공유) 정책을 따르기 위해 사전 요청을 보낸다. 이 요청은 브라우저가 API에 대해 허용된 옵션을 묻는 요청이다. (예: 누가 API를 호출할 수 있는지, 어떤 유형의 요청이 가능한지)
3. API가 옵션과 캐시 기간을 포함한 응답을 보냄
백엔드 API는 브라우저의 사전 요청에 대한 응답으로 허용된 옵션을 포함한 정보를 보낸다. 추가로 브라우저가 이러한 설정을 캐시하고 동일한 사전 요청을 반복하지 않도록 캐시 기간을 설정할 수도 있다.
4. 브라우저가 요청을 허용함
만약 해당 요청이 허용된 목록 내에 있다면, 브라우저는 이 요청을 허용하고, 요청은 정상적으로 백엔드 API로 전송된다.
5. 요청이 거부되는 경우
해당 요청이 허용된 목록에 포함되지 않은 경우 브라우저는 요청을 차단하고 사용자는 오류 메시지를 받게 된다.
이 단계를 통해 알 수 있는 것은 두 가지 이다.
1. 브라우저를 건너 뛰고 직접 요청을 보내면 CORS 정책을 쉽게 우회할 수 있다는 점
curl, Postman, 또는 기타 HTTP 클라이언트를 사용하는 경우 CORS 정책이 우회되는 이유이다. 이런 도구들은 웹 브라우저가 아니기 때문에 브라우저의 보안 규칙을 따를 이유가 없다.
위 예제에서 A의 개인번호를 알고 있는 A의 친구가 A에게 바로 전화를 건 상황을 생각해보자. 이 상황이 바로 브라우저의 CORS 정책을 무시하고 직접 HTTP 요청을 보낸다는 의미이다.
2. 브라우저는 실제 요청을 보내기 전에 사전 요청(Pre-flight Request) 을 먼저 진행한다는 점
여기서 사전 요청(Pre-flight Request)이란 무엇일까?
사전 요청(Pre-flight Request)이란 본 요청을 보내기 전에 브라우저 스스로 안전한 요청인지 미리 확인하는 것이다.
요청에 대한 옵션은 아래와 같다.
- Origin 헤더에 자신의 출처를 넣는다.
- Access-Control-Request-Method 헤더에 실제 요청에 사용할 메소드를 설정한다.
- Access-Control-Request-Headers 헤더에 실제 요청에 사용할 헤더들을 설정한다.
사전 요청(Pre-flight Request)은 OPTIONS 메서드를 사용해 요청을 보내고 이 요청에 대한 응답으로 어떤 것을 허용하고 어떤것을 금지하고 있는지에 대한 헤더 정보를 담아서 브라우저로 보내준다.
응답에 대한 옵션은 아래와 같다.
- Access-Control-Allow-Origin 헤더에 허용되는 Origin들의 목록을 설정한다.
- Access-Control-Allow-Methods 헤더에 허용되는 메소드들의 목록을 설정한다.
- Access-Control-Allow-Headers 헤더에 허용되는 헤더들의 목록을 설정한다.
- Access-Control-Max-Age 헤더에 해당 예비 요청이 브라우저에 캐시 될 수 있는 시간을 초 단위로 설정한다.
이후 브라우저는 보낸 요청과 서버가 응답해준 정책을 비교하여, 해당 요청이 안전한지 확인하고 본 요청을 보내게 된다.
마지막으로 서버가 본 요청에 대한 응답을 하면 최종적으로 이 응답 데이터를 자바스립트로 넘겨준다.
한 가지 덧붙이자면, 사전 요청(Pre-flight Request)은 다음 조건 중 하나를 충족할 때만 사전 요청을 보낸다.
1. 특정 유형의 요청인 경우
특정 유형의 요청에만 해당된다. (ex. POST, PUT, DELETE )
2. 커스텀 헤더가 포함된 경우
요청에 X-Custom-Header와 같은 커스텀 헤더가 포함되어 있을 때 사전 요청(Pre-flight Request)이 보내진다.
3. 특정 Content-Type 값을 가지는 경우
요청의 Content-Type 헤더가 application/x-www-form-urlencoded, multipart/form-data, 또는 text/plain 이외의 값을 가질 때 사전 요청(Pre-flight Request)이 보내진다. (ex application/json)
CORS가 없다면?
그렇다면 CORS가 없다면 발생할 수 있는 예시는 어떤 것이 있을까?
오늘이 월급날이라고 가정해보자.🤑우리는 즐겁게 은행 계좌에 로그인을 해서 잔액을 확인하고 돈이 입금된 것을 확인할 것이다.
그런데 갑자기! 좋아하는 옷이 대폭 할인 된다는 설명과 사이트 링크(사기 링크)가 있는 어떤 메일이 도착한다.
이후에 링크를 클릭해보니 옷에 관련된 페이지가 아닌 빈 페이지가 나타나게 된다.
만약 은행이 CORS를 활성화 한 경우, 브라우저는 이 사기 링크 도메인이 은행의 API를 호출할 수 있는지 확인한다. 확인 결과, 도메인이 허용되지 않았기 때문에 요청이 거부되고 돈을 지킬 수 있다.
이러한 예시를 전문 용어로 CSRF(Cross-Site Request Forgery, 크로스 사이트 요청 위조) 라고 부르며 사용자의 인증된 세션을 악용하여 공격하는 방식을 말한다.
여기서 동일 출처 정책이라는 개념이 등장하는데, 동일 출처 정책은 CSRF 공격을 막기 위해 발명되었다.
SOP(Same-Origin Policy, 이하 동일 출처 정책)
SOP(Same Origin Policy) 정책은 단어 그대로 동일한 출처에 대한 정책을 말한다. 그리고 이 SOP 정책은 '동일한 출처에서만 리소스를 공유할 수 있다.'라는 법률을 가지고 있다.
동일 출처(Same-Origin) 서버에 있는 리소스는 자유로이 가져올수 있지만, 다른 출처(Cross-Origin) 서버에 있는 이미지나 유튜브 영상 같은 리소스는 상호작용이 불가능하다는 말이다. SOP 정책이 없었다면 위와 같이 CSRF 공격을 막을 수 없다.
그렇다면 두 개의 출처의 다름 유무를 판단하는 기준은 무엇일까?
간단하다. 요청을 보낼 때 프로토콜, 호스트, 포트이 3가지만 일치하면 동일한 출처로 간주된다.
http://example.com/script.js 에서 출발한다면 예시는 다음과 같다.
- ✅ http://example.com/api/
- ⛔ http://example.org/api/
- ⛔ http://api.example.org/
- ⛔ http://example.com:8000/api/
- ⛔ https://example.com/api/
그렇다면 CORS는?
동일 출처 정책은 월드 와이드 웹의 초창기 구조이다.
오늘날 우리가 알고 있는 인터넷은 콘텐츠, 콘텐츠 전송 네트워크, 단일 페이지 디자인, 좋아요 및 공유 등의 풍부한 생태계로 폭발적으로 성장했다.
이 다양성과 변화를 지원하기 위해 동일 출처 정책도 확장하고 적응해야 했다.
그러나 CORS가 바로 등장한 것은 아니다.
XmlHTTPRequest
XmlHTTPRequest은 웹을 더 풍부하게 만들기 위해 HTTP 통신을 할 수 있도록 하는 API이다. 매번 페이지를 다시 로드할 필요 없이 리소스를 비동기적으로 불러올 수 있다.
통신할 때 발생할 수 있는 잠재적 위험을 감안하면 XmlHTTPRequest는 동일 출처 정책 규칙을 엄격하게 적용해야 하는 영역이기도 하다. 따라서 XmlHTTPRequest는 동일 출처 정책의 요구사항이 완전히 적용되었다.
- 다른 출처 사이트로 호출할 수는 있었지만, 응답을 읽을 수 없다.
- 요청 URL이 같은 출처에 있다면 응답을 읽을 수 있다.
- 사용자 지정 헤더는 같은 출처에 대한 요청에만 추가할 수 있다.
JSONP
앞서 XmlHTTPRequest는 다른 출처에 대한 비동기 통신을 만들 수는 있지만 응답을 읽을 순 없다. 그렇다면 다른 사이트에서 받은 환율, 일기 예보, 앨범 목록 등의 기타 정보는 어떻게 표시할 수 있었을까?
이를 위해 <script> 를 사용하여 다른 출처의 데이터를 불러오게 된다. 이 방법은 동일 출처 정책에 어긋나지만 동일 출처 정책과 관계없이 데이터를 불러올 수 있다.
하지만 JS 문법 오류가 발생하며, 또한 보안 위험을 초래한다. JSONP 요청이 실행되면 JS는 해당 리소스에서 반환된 모든 항목을 신뢰할 수 있다고 가정하기 때문이다.
CORS의 등장
따라서 JSONP의 한계 및 보안 문제를 해결하기 위해 등장한 것이 CORS이다.
Microsoft는 IE8~9에서 XDomainRequest가 솔루션이 되었고, Chrome, Firefox 등 주요 브라우저에서는 CORS라는 대체 기능을 구현했다. (이후에 Microsoft에서도 CORS의 잠재력과 인기를 인식하고 IE10 이상에 채택하게 되었다.)
오늘은 CORS가 무엇인지, CORS가 등장하게 된 배경에 대해 다뤄보았다. 여러 글을 접했지만 쉽게 와닿지 않았던 개념이었는데 정리해보니 어떤 건지 좀 더 잘 이해할 수 있게 된 것 같다.
참고
https://medium.com/itnext/understanding-cors-4157bf640e11
https://medium.com/gitconnected/lets-talk-cors-what-you-need-to-know-2e8119114a54
https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F
https://evan-moon.github.io/2020/05/21/about-cors/
https://blog.areumsheep.vercel.app/contents/why-cors/
'Tips > IT 상식' 카테고리의 다른 글
[IT 상식] 프레임워크와 라이브러리의 차이 (0) | 2024.07.20 |
---|---|
버퍼(Buffer)란? (3) | 2024.04.08 |
HTTP 멱등성이란? (1) | 2024.04.06 |
하드코딩이란? (0) | 2024.04.02 |
- Total
- Today
- Yesterday
- 동기
- js
- 배열
- innerhtml
- 비동기
- 유사배열객체
- CSS
- currentTarget
- map
- Git
- 객체
- 코드잇 스프린트
- 스프린트프론트엔드6기
- Next.js
- 취업까지달린다
- Target
- 비제어 컴포넌트
- javascript
- arguments
- tanstackquery
- html
- 리액트
- 코드잇스프린트
- 제어 컴포넌트
- 중급 프로젝트
- GitHub
- hydrationboundary
- 프론트엔드
- rest parameter
- react
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 29 | 30 |