CORS란 무엇일까?
글을 쓰기 앞서 나는 CORS로 크게 고생해본 적이 없다. 처음부터 헤더값을 넣어주기도 했고, 개인 프로젝트를 진행할 땐 프록시를 써서 우회했기 때문이다.
하지만 CORS라는게 무엇인지에 대해선 정리해본 적이 없어서
- CORS란 무엇일까?
- CORS는 왜 필요한걸까?
이번 기회에 이 두가지 질문에 대한 내용을 한번 작성해보려 한다.
CORS는 왜 필요한걸까?
다음과 같은 상황이 있다고 가정해보자.
!🤑 정말 행운입니다 🤑!
당신은 인터넷 서핑 도중, CSKIM999.com이라는 사이트에서 당신의 주거래은행에 숨어있는 말도안되는 이율의 대출상품에 대해 알게 되었습니다! 사람들이 몰리기 전에 빨리 해당 대출상품을 받아야 할 것 같습니다!정보를 알게 된 직후 당신은 대출한도를 확인하기 위해 당신의 주거래은행 온라인 뱅킹서비스에 로그인했습니다.
하지만 아무리 찾아도 해당 대출상품은 없었습니다.
뒤늦게 해당 상품을 인터넷에 검색해보지만 CSKIM999.com의 게시물만 나올 뿐이었습니다.
크게 실망한 당신은 우연히 당신의 잔고를 확인하게 됩니다. [ 0 원 ]
어떻게 된 걸까요? 거래내역을 확인해보니 CSKIM999 라는 사람에게 자산이 모두 송금되어있습니다.
!🤯 말도 안돼 🤯!
위 상황을 간단하게 요약하자면, 공격 코드가 심겨있는 사이트 CSKIM999.com을 방문한 나는 동시에 다른 탭으로 은행에 로그인한다. 모든 인증정보를 가지고 악성코드는 출금요청을 보낸다.
은행측에서는 실제로 고객이 보낸 요청인지, 악성유저가 보낸 요청인 지 알 수 없기때문에 출금을 승인한 것이다.
실제로 우리는 이렇게 위험한 인터넷 생활을 하진 않는다. 하지만 위 상황은 오늘 알아 볼 SOP라는 정책이 없을 때 발생할 수 있는 CSRF 라는 공격방법 중 하나다.
CORS에 대해서 설명하기 이전에 매우 밀접한 관계에 놓여있고 CORS가 존재하는 이유인 SOP에 대해서 먼저 설명하고자 한다.
SOP [ Same Origin Policy ]
SOP의 뜻을 그대로 해석해보자면 ‘동일 출처 정책’ 정도가 될 것인데, 말 그대로 SOP는 다른 출처로 요청을 보낼 수 없도록 금지하는 브라우저의 기본적인 보안정책이다.
인터넷의 극초기엔 현재처럼 서버 혹은 다른 서비스와 통신을 주고받을 이유가 없었다. 모든 정보를 하나의 페이지(HTML)에 구겨넣었고, 따라서 교차 출처 요청을 할 이유도 없었다.
하지만 기술이 발전하며 더욱 다양한 서비스를 제공하기 위해 동일출처인 웹서버 뿐만 아니라 백엔드서버(WAS), 또는 다른 서비스에 요청을 보내고 그 응답값을 바탕으로 또 다른 데이터를 제공해야하는 복잡도가 올라갔다.
이 쯤에서 궁금증이 발생할 수 있다. 계속 나오는 키워드인 ‘출처(Origin)’ 가 정확히 무엇을 의미할까?
그리고 그 출처를 어떤식으로 확인해서 접근을 제한하고 허용할 수 있는걸까?
Origin(출처)는 URL 에서 프로토콜, 도메인, 포트번호를 합친 부분을 의미한다.
아래의 예시 URL 에서 Origin을 찾아보자
https://dev.mockurl.com:80/document/5523
프로토콜은 https://, 도메인은 dev.mockurl.com, 포트번호는 :80 일 것이다.
따라서 Origin 은 https://dev.mockurl.com:80 이 된다.
그 뒤의 document 나 5523 은 단순히 리소스 식별자(path)이기 때문에 Origin 확인에서는 제외된다.
앞선 예시와 마찬가지로 만약 다른 출처로 요청을 보낼 수 있다면 사용자의 개인정보, 계좌 잔액, 그 외 모든것들을 탈취할 수 있다. 따라서 브라우저는 강제로 동일 출처의 접근만 허용하는 정책을 만든다. 그것이 바로 Same Origin Policy이다.
하지만 우리는 지금 웹서버, 백엔드서버(WAS), API 가릴 것 없이 많은 서비스에 접근하고있다. 이는 SOP에 위배하는 방법인데 도대체 어떻게 그게 가능한 것일까?
CORS [ Cross Origin Resource Sharing ] 란 무엇일까?
두 가지 질문 중 핵심질문이었던 CORS란 무엇인가? 에 대해서 이야기해보자.
SOP를 제한적으로 회피할 수 있도록 해주는방법이 바로 CORS 이다. 이 또한 뜻을 단어 그대로 해석해보자면 ‘교차 출처 자원 공유’ 이며,
현재 자신의 Origin과 서버에서 작성한 응답헤더에 담겨있는 Origin을 비교하고, 만약 Origin이 같다면 해당 자원에 대해서 SOP 정책의 제한적인 규칙 내에서 안전하게 공유시켜주는 HTTP 헤더 기반 메커니즘이다.
즉, CORS는 브라우저를 위해 현재 브라우저에서 접근하려는 리소스가 SOP 안에서 안전한지 HTTP 헤더를 통해 확인하는 기능이다.
그렇다면 CORS에러는 어떻게 해결해야 하는가?
CORS는 브라우저와 서버가 상호 협의하에 허용을 하는 과정이기때문에, 서버에서 적절한 헤더를 보내주는 것이 중요하다.
서버에서 Access-Control-Allow-Origin 헤더에 현재 우리 서비스의 Origin을 명명한다면 브라우저는 자동으로 요청 및 응답 과정에서 Origin을 비교하고 만약 일치하지 않는다면 CORS 에러를 내뿜는다.
즉, 우리 FE 측에서는 CORS에러가 발생했을 때 할 수 있는 일이 딱히 없다!
BE에게 CORS에러가 발생했으니 헤더값을 빨리 수정해달라는 요청만 하면 된다.
물론 프록시를 사용하거나 다른 여러가지 방법이 있겠지만, 권장하는 방법은 서버측에서 Access-Control-Allow-Origin 헤더를 적절히 입력해주는 것이다.
하지만 로그인과 같은 기능에서 자격증명이 필요해진다면 이야기가 달라진다.
쿠키와 같은 브라우저 정보를 필요로 한다면 해당 정보까지 함께 전달한다는 옵션을 헤더에 제공해야하기 때문이다. 하지만 이 부분은 CORS와는 조금 거리가 있는 내용이니 넘어가도록 하겠다.
추가로 흥미로운 사실
SOP는 브라우저의 보안정책이다. 따라서 브라우저가 아닌 Native 개발환경인 Flutter 나 React Native에서는 SOP가 없기 때문에 CORS에러 또한 발생하지 않는다.
결론 - 3줄요약
정리글의 마지막엔 매번 소감같은 몇줄의 글을 작성하곤 했는데, 보는사람은 딱히 궁금해하지 않을 내용인 것 같았다.
그래서 이번엔 일단 문서의 맨 끝까지 스크롤을 드르륵 내렸을지도 모르는 당신을 위해,
혹은 한줄 한줄 잘 읽어줬을 당신을 위해 3줄요약을 준비해봤다.
- SOP 는 웹 보안정책의 기본 요소로 다른 출처로의 요청을 제한하여 사용자 정보를 보호하는 역할을 한다.
- CORS는 SOP의 제한을 안전하게 우회하는 기능으로 특정 출처의 자원에 대한 접근을 허용하여 다양한 서비스와의 통합과 상호작용을 가능하게 한다.
- CORS에러는 SOP를 위반했으니, 의도한 동작이라면 CORS를 설정해서 우회해라! 를 의미한다.
