CORS는 언제까지 날 괴롭힐까

우빈·2023년 10월 14일
12
post-thumbnail

글을 쓰게 된 계기

최근에 해커톤에 참여하게 되었는데, 그 때에 같은 팀의 백엔드 개발자와
API 통신을 진행할 때 개발자님이 CORS 설정을 해놓았는데도 불구하고
CORS 이슈가 발생해 글을 써보아야겠다는 생각을 했다.

저번 프로젝트를 진행할 때에도 AWS에 테스트 서버를 배포해서 이를
사용한 적이 있었는데, 이 때도 알 수 없는 CORS 이슈가 발생했었다.

글을 쓰게 된 계기를 요약하자면...
1. 프로젝트를 할 때마다 CORS가 뜨고 종류가 다 달라서 트러블 슈팅하기가 어려웠음
2. 프론트엔드를 맡을 때마다 CORS가 뜨면 withCredentials 속성만 바꿀 수 있는 무능한 내가 부끄러웠음
3. 포스트맨에서는 잘 되는데 왜 로컬호스트에서는 안되는지 모르겠음

이제부터 CORS에 대해 잘 정리해보자.
P.S 글 안쓴지 진짜 오래됐는데 반성하겠다... 이제부터 다시 글 열심히 쓸게

CORS?

CORS는 Cross Origin Resource Sharing의 약자로,
개발을 진행하다보면.. 꼭 한 번 쯤은 겪어 볼 수 있는 에러이다.
도대체 이 에러는 무엇이고, 왜 발생하는 것일까?

보통 나는 새로운 것을 배울 때 용어의 의미를 분해해서 해석한다.
Cross Origin Resource Sharing,
Origin을 출처라고 생각해보자.

그렇다면 교차된 출처의 자원들을 공유하는 정책..?이 CORS라 할 수 있겠다.
대충 감이 오지 않는가?

CORS란

CORS는 특정 도메인의 웹 페이지가 다른 도메인을 가진 자원을
사용할 수 있게 도와주는 보안 정책이다.

도대체 CORS가 필요한 이유가 뭐야?

기존에는 웹사이트에서 많은 일을 수행하지 않았지만,
요즘 웹 사이트에서는 여러가지 일을 수행하다보니 나의 웹 사이트에서
다른 웹 사이트가 주는 자원들을 사용하는 일도 많아졌다.

그래서 동일한 도메인간의 요청만 할 수 있었던 예전과는 달리,
다른 사이트에도 요청을 할 수 있는 대신 CORS 정책이 만들어졌다.

CORS가 없다면?

CORS가 없다면 우리는 세상에 존재하는 모든 사이트에 요청을 보낼 수 있다.

요즘 브라우저는 개발자 도구를 키면,
웹 사이트가 어떤 코드로 구성되어있는지 쫙 나오기 마련이다.

어떤 한 사람이 이를 악용해 XSS, CSRF같은 방식으로 우리 사이트에
정상적인 요청인 척 임의의 요청을 보낸다면...

그 요청으로 정보를 탈취할 수 있는 것이다..!
그래서 CORS가 등장했다.

CORS는 그럼 누가 발생시키는 것일까? 서버? 클라이언트?

응답을 막는 주체는 브라우저이다

우리의 응답이 CORS 정책에 위반된다며 막는 주체는 서버가 아닌 브라우저이다.
따라서 브라우저를 통하지 않는 모든 통신은 CORS에 위반되는 것이다.

서버는 response를 반환했음에도 불구하고, 브라우저가 이 응답이
요청한 도메인과 다르면 응답을 폐기하는 방식이다.

그럼 CORS는 어떻게 작동할까?

CORS의 동작 방식

먼저 요청에는 세 가지 종류의 요청이 있다.
Simple Request, Preflight Request, Credentialed Request.

먼저 Simple Request에 대해 알아보자.

Simple Request

Simple Request는 말 그대로 간단한 요청이다.
클라이언트에서 요청을 보내면, 브라우저가 그 요청을 받아 서버에게 전달한다.
서버는 이에 대해 클라이언트에 응답을 보낼 때 헤더에 정보를 넣어 준다.

Access-Control-Allow-Origin이라는 헤더 속성에,
"이 자원에 접근하는 게 허용된 출처입니다~~"라는 것을 명시하고 전송한다.

그렇게 되면 브라우저가 클라이언트가 보냈던 출처와,
서버가 응답하며 헤더에 위치한 Access-Control-Allow-Origin 헤더를
비교해서 유효한 응답이 맞는지 아닌지 판단한다.

예를 들면 서버에서는 Access-Control-Allow-Origin: *
이와 같은 방식으로 200을 함께 보낸다.

와일드카드는 누구나 이 자원에 접근할 수 있음을 허용한다는 뜻이다.
혹은 저 속성에 허용할 출처를 명시해주는 방법도 있다.

와일드카드를 너무 많이 사용할 경우 누구나 자원을 가져갈 수 있기 때문에,
속성을 허용할 출처를 명시해주는 것도 괜찮은 방법이다.

브라우저가 이를 Simple Request라고 인식하는 조건이 세 가지가 있다.

  1. HTTP methodGET, POST, HEAD 중 하나이다
  2. Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width 헤더만 사용했다
  3. Content-Typeapplication/x-www-form-urlencoded, multipart/form-data, text/plain 중에 하나이다

이를 어기면 Simple Request로 인식하지 않는데, 우린 생각보다
개발을 하며 이 조건을 지키기가 어렵다.

헤더에 Authorization같은 속성을 사용할 때도 있으며,
다른 HTTP 메서드를 사용할 때도 있고, Content Type을
application/json으로 사용하는 경우도 많기 때문이다.

그렇다면 Simple Request가 아니라면 어떤 요청일까?

Preflight Request

Simple request가 아니면서 다른 도메인에 접근하는 요청은
대부분 Preflight request 방식을 따른다.

Preflight 요청은 HTTP 메서드 중 OPTIONS라는 메서드를 사용해서,
미리 요청을 한번 보내놓고 실제 요청을 보낼지 말지 판단하는 요청이다.

OPTIONS 요청을 보내면 서버는 응답으로 Access-Control-Allow-Origin
헤더를 포함해서 응답을 브라우저에게 보낸다.

그럴 경우 브라우저는 헤더를 확인 후 진짜 요청을 보낼지 말지 결정하는 것이다.

Credentialed Request

Credentialed Request는 인증과 관련된 정보를 담을 수 있게 해주는
옵션이다.

프론트엔드가 axios를 사용한다고 가정할 때, withCredentials라는 속성을
true로 설정했을 때 인증과 관련된 정보를 담을 수 있게 해주는 것이다.

그렇다면 어떻게 해결해?

해결하는 방법은 다양하다. 백엔드에서 Access-Control-Allow-Origin을
따로 명시해주는 방법도 좋은 방법이지만, 그렇지 못할 경우는

프론트엔드에서 프록시 서버를 돌리는 방법이 있다.
프록시 서버를 돌리면 CORS가 왜 해결이 될까?

바로 아까 말한 것처럼 CORS는 브라우저가 발생시키는 에러이기 때문이다.
따라서 프록시 서버를 만들어 Server to Server로 API를 전달하면
브라우저의 범위를 벗어나서 CORS 에러를 피할 수 있다!

마무리

CORS는 프로젝트를 할 때마다 나를 괴롭혔었는데,, 이렇게 글을 정리하고
보니 다음 프로젝트 때에는 백엔드 개발자와 더 정확하게 어떤 문제가 있는지
커뮤니케이션을 해볼 수 있을 것 같은 자신감이 생겼다.

다음부터 CORS 이슈가 또 뜨면 Access-Control-Allow-Origin부터
확인해보아야겠다..

모두 개발 화이팅ㅇ!!!

profile
프론트엔드 공부중

0개의 댓글