Web 필수 지식 - CORS

로건·2022년 9월 9일
0

Web

목록 보기
1/1

CORS(Cross-Origin Resoure Sharing) 동일한 출처가 아닐때 검사하는 정책이라고 두루뭉술하게 알고 있었는데 프로젝트 진행 중 CORS 때문에 2,3일 고생하면서 제대로 습득해야하는 지식이라는 걸 뼈저리게 깨달았다. 개념과 동작방식, 평소 궁금했던 내용들도 함께 정리해본다.



웹의 Origin(출처) Policy

먼저 Origin이 무엇인지에 대해서 명확히 알고 갈 필요가 있다.
아래의 사진은 URL의 구성요소이다. 이 URL중 Protocol + Host + Path을 합친 부분을 Origin한다. 이 중 단 하나라도 다르다면 다른 출처인 것이다.

예를 들어,
https://naver.comhttp://naver.com 은 프로토콜이 다르므로 다른 출처이다.


🤔 SOP - 브라우저 Origin Policy

웹은 크게 두가지의 Origin 정책을 가지고 있다. 그 중 첫번째가 SOP(Same-Origin Policy)이다.

브라우저는 서버로 요청을 보낼때 같은 출처인지 확인 하는데 이것이 SOP이다.
만약 다른 출처라면 브라우저는 다음에 설명할 CORS 정책을 따라 요청을 시도할 것이다.

확실히 알아야 할것은 출처를 비교하는 것은 서버의 역할이 아니고 브라우저가 하는 역할이다. 서버로 보내기전 브라우저에서 체크를 하는 것이다.

💡 IE(Internet Explorer)의 Origin 비교
위에서 설명했듯이 Orign은 Protocol + Host + Port이다. 하지만 IE에서 Origin을 비교할때는 Port른 비교하지 않는다. 즉, Protocol + Host만을 이용해서 Origin을 비교한다


🤔 CORS - 브라우저 Origin Policy

CORS(Cross-Origin Resource Sharing)은 서버측에서 헤더를 통해서 다른 출처(웹)에서 자원에 접근할 수 있는 권한을 부여하도록 브라우저에게 알려주는 정책이다.

브라우저가 CORS를 확인하는 방법은 아래와 같다

  1. 웹에서 서버로 요청
  2. 브라우저에서 요청 헤더에 Origin을 추가로 담아서 보낸다.
  3. 서버에서는 보내는 응답 헤더 안에 Access-Control-Allow-Origin 넣어서 보낸다.
  4. 브라우저는 요청시 보낸 Origin과 응답 헤더 안에 담긴 Access-Control-Allow-Origin의 값을 비교한다
  5. 만약 Access-Control-Allow-Origin안에 Origin이 포함되지 않는다면 브라우저가 해당 응답을 버리고 CORS Policy를 위반했다는 에러를 console에 뿌려준다.

위 과정에서 알다시피 클라이언트-서버의 소통은 정상적으로 성공하였으므로 서버의 로그에도 정상적으로 응답했다고 찍힐 것이다. CORS를 제대로 이해하지 못하고 있다면 굉장히 골치 아파질 것이고 필자가 그러하였다.(내 2일 돌려줘)




CORS 동작방식 제대로 알아보기

CORS 정책이 동작하는 방식을 조금 더 자세히 알아보겠다.
이 파트를 정확히 숙지해야 console에서 나는 오류메시지를 보고 CORS 정책 중에서 어떤 부분을 위배되었는지 빠르게 알아차릴 수 있다. 필자는 이 부분도 제대로 숙지하지 못해서 많은 고생을 했다. 흑ㅠㅠ

CORS는 상황에 따라서 Preflight Request, Simple Request, Credentialed Request 이 3가지 요청방식을 사용한다.


🤔 Preflight Request

예비 요청이라고 부르는데 Preflight Request로 알고 있는게 오류 메시지를 보고 문제를 알아차리기 편하다. 이 요청에 대해 위배가 되면 console 메세지에 preflight response라는 단어가 들어가는 오류를 뿜어준다.

브라우저에서는 서버로 요청을 보낼때 두 번의 요청을 보낸다. 예비요청과 본 요청이다. 물론 다음에 설명할 Simple Request만 보내는 상황도 있지만 해당 개념을 설명할때 자세히 설명하겠다.

예비 요청으로는 본요청의 METHOD가 아닌 OPTIONS 메서드를 이용한다.
이 요청은 본 요청을 보내기 전에 브라우저가 요청을 보내는 것이 안전한지 확인하는 과정이다.
과정을 살펴보자면

  1. 웹에서 API 요청
  2. 브라우저에서 예비요청을 보냄
  3. 서버에서는 응답으로 어떤 메서드를 허용할 것인지, 어떤 헤더를 허용할 것인지 등을 보내준다.
  4. 브라우저는 예비 요청의 응답을 확인하고 본 요청을 보낸다.
  5. 본 요청을 받고 응답 데이터를 웹으로 정상적으로 보낸다.
  6. 실패한다면 당근 console에 오류메시지를 뿜어줄 것이다.

🤔 Simple Request

단순요청은 위에서 설명한 예비요청을 보내지 않고 바로 본요청을 서버로 보내는 경우이다. 브라우저는 응답의 헤더를 보고 CORS 정책을 위반하는지 검사한다.
그럼 언제 예비 요청을 보내고 언제 단순 요청을 보낼까?
단순요청이 동작되는 경우는 매우 까다롭다. 아래의 경우를 모두 만족할 경우에만 단순요청을 보낸다.

  • GET, HEAD, POST의 요청이어야 하며
  • Custom Header 즉, 브라우저가 추가하는 헤더가 아닌 직접적으로 추가한 헤더가 있으면 안된다.
  • Content-Type는 다음의 값들만 가능하다.
    - application/x-www-form-urlencoded
    - multipart/form-data
    • text/plain

Content-Type은 대부분 application/json으로 통신하기 때문에 단순 요청이 일어나기는 쉽지 않다.


🤔 Credentialed Request

예비 요청에서 보안을 강화할때 사용한다.
기본적으로 웹에서 요청하는 API들은 옵션을 지정해주지 않으면 브라우저 내의 쿠키 나 인증관련된 헤더를 담지 않는다.
이런 정보들을 담을 수 있게 해주는 것이 credential이다. 값으로는
same-origin 같은 출처 간 요청에만 인증 정보를 담을 수 있음
include 모든 요청에 인증 정보를 담음
omit 모든 요청에 인증 정보를 담지 않음.

이 옵션을 사용한다면 CORS 검사하는 룰이 추가된다

  • 서버 응답헤더에 Access-Control-Allow-Origin의 값으로 와일드카드(*)를 사용할 수 없다. 명시적인 URL을 작성해야한다
  • 서버 응답헤더에 Access-Control-Allow-Credentials라는 헤더와 그 값아 true로 설정되어 있어야한다.



More and More

🤔 이 복잡합 CORS 처리는 왜하는 걸까

CORS가 존재하지 않고 모든 곳에서 데이터 요청이 가능하다면 다른 사이트에서 원본을 흉내내고 세션을 탈취해 공격이 가능해지게 된다. 이런 공격을 브라우저가 보호하기 위함이며 필요한 경우에는 서버측과 협의하여 요청을 보내기 위해서 존재한다.

🤔 왜 모바일이나 Post man에서는 에러가 안나는가

CORS는 말 그대로 브라우저의 정책이다. 모바일이나 Post man은 브라우저를 거치지 않기 때문에 브라우저가 하는 역할을 수행하지 않을 것이다.

🤔 Access-Control-Request-Headers

브라우저가 요청시 추가하는 요청헤더이다. 어떤 커스텀 헤더들이 들어가는지 명시되어 있다.

🤔 CORS 해결 방법

  • 로컬에서 개발시 크롬 익스텐션을 이용해 CORS 정책 사용을 끄는 방법이 있지만, 실제 배포하고 나서 문제가 된다. 애초부터 사용하지 않는 것을 권장한다. 사실상 해결방법이 아닌 지금 당장 동작시키기 위한 반창고 붙이기 밖에 되지 않는다.
  • 중간에 proxy서버를 거쳐가는 방법이 있다. 브라우저 - proxy - 서버 순으로 동작하게 하여 proxy서버측에서 서버에서 받은 응답헤더를 조작하여 브라우저에게 보내주는 방법이다.
  • 가장 좋은 방법으로 서버의 응답헤더에 재대로 추가해주는 것이 좋다. 응답헤더의Allow-Control-AlLow-Origin 값에 가능한 교차출처들을 넣어주는것이 좋다.

참고 블로그
even-moon님의 블로그
인파_님의 블로그

profile
Life Designer

0개의 댓글