[웹 스터디] CORS

박준수·2022년 12월 16일
0

[웹 스터디]

목록 보기
16/19
post-thumbnail

CORS(Cross-Origin Resource Sharing)

: 처음 리소스를 제공한 도메인이 현재 요청하려는 도메인과 다르더라도 요청을 허락해주는 웹 보안 방침이다.

출처(Origin)이란..?


이때 출처는 Protocol과 Host, 위 그림에는 나와있지 않지만 :80, :443과 같은 포트 번호 까지 모두 합친 것을 의미한다. 또한 출처 내의 포트 번호는 생략이 가능한데, 이는 각 웹에서 사용하는 HTTP, HTTPS 프로토콜의 기본 포트 번호가 정해져 있기 때문이다.

간단히 출처(Origin) 라는 것은 Protolcol 과 Host 그리고 Port 까지 모두 합친 URL을 의미한다고 보면 된다.

SOP(same-Origin Policy)

SOP는 말 그대로 "같은 출처에서만 리소스를 공유할 수 있다" 라는 규칙을 가진 정책이다.

그러나 웹이라는 오픈스페이스 환경에서 다른 출처에 있는 리소스를 가져와서 사용하는 일은 굉장히 흔한 일이라 무작정 막을 수도 없는 노릇이나 몇 가지 예외 조항을 두고 이 조항에 해당하는 리소스 요청은 출처가 다르더라도 허용하기로 했는데, 그 중 하나가 "CORS 정책을 지킨 리소스 요청"이다.

SOP가 필요한 이유

사실 출처가 다른 두 어플리케이션이 자유로이 소통할 수 있는 환경은 꽤 위험한 환경이다.
다른 출처의 어플리케이션이 서로 통신하는 것에 대해 아무런 제약도 존재하지 않는다면, 악의를 가진 사용자가 소스 코드를 쓱 구경한 후 CSRF(Cross-Site Request Forgery)나 XSS(Cross-Site Scripting)와 같은 방법을 사용하여 여러분의 어플리케이션에서 코드가 실행된 것처럼 꾸며서 사용자의 정보를 탈취하기가 너무나도 쉬워진다.

같은 출처와 다른 출처의 구분

두 URL의 구성 요소 중 Scheme, Host, Port, 이 3가지만 동일하면 된다.

예를 들면)
https://evan-moon.github.io:80라는 출처를 예로 들면 https:// 이라는 스킴에 evan-moon.github.io 호스트를 가지고 :80번 포트를 사용하고 있다는 것만 같다면 나머지는 전부 다르더라도 같은 출처로 인정이 된다는 것이다.

출처 비교와 차단은 브라우저가 한다.


여기서 중요한 사실 한 가지는 이렇게 출처를 비교하는 로직이 서버에 구현된 스펙이 아니라 브라우저에 구현되어 있는 스펙이라는 것이다.

만약 우리가 CORS 정책을 위반하는 리소스 요청을 하더라도 해당 서버가 같은 출처에서 보낸 요청만 받겠다는 로직을 가지고 있는 경우가 아니라면 서버는 정상적으로 응답을 하고, 이후 브라우저가 이 응답을 분석해서 CORS 정책 위반이라고 판단되면 그 응답을 사용하지 않고 그냥 버리는 순서인 것이다.

즉, CORS는 브라우저의 구현 스펙에 포함되는 정책이기 때문에, 브라우저를 통하지 않고 서버 간 통신을 할 때는 이 정책이 적용되지 않는다. (서버 쪽 로그에는 정상적으로 응답했다는 로그가 남)

CORS의 동작 방식

Preflight Request

사실 브라우저는 요청을 보낼때 한번에 바로 보내지않고, 먼저 예비 요청을 보내 서버와 잘 통신되는지 확인한 후 본 요청을 보낸다. 즉, 예비 요청의 역할은 본 요청을 보내기 전에 브라우저 스스로 안전한 요청인지 미리 확인하는 것이다. 이때 브라우저가 예비요청을 보내는 것을 Preflight라고 부르며, 이 예비요청의 HTTP 메소드를 GET이나 POST가 아닌 OPTIONS라는 요청이 사용된다는 것이 특징이다.

  1. 자바스크립트의 fetch() 메서드를 통해 리소스를 받아오려고 한다.
  2. 브라우저는 서버로 HTTP OPTIONS메소드로 예비 요청을 먼저 보낸다.
  3. 서버는 이 예비 요청에 대한 응답으로 어떤 것을 허용하고 어떤 것을 금지하고 있는지에 대한 헤더 정보를 담아서 브라우저로 보낸다.
  4. 이후 브라우저는 보낸 요청과 서버가 응답해준 정책을 비교하여, 해당 요청이 안전한지 확인하고 본 요청을 보내게 된다.
  5. 서버가 본 요청에 대한 응답을 하면 최종적으로 이 응답 데이터를 자바스크립트로 넘겨준다.

이 프로젝트의 Todo 애플리케이션에서 프론트엔드 서버의 도메인은 http://localhost:3000이다. 따라서 현재 Todo 페이지의 Origin이 http://localhost:3000이다. 하지만 백엔드 서버의 도메인은 localhost:8080이다. 도메인이 다르므로 요청을 거절한다.

코드설명

  • HTTP 요청을 할 때 보낼 옵션을 생성한다. (requestOptions)
  • Backend 진행할 때 만든 TodoController 파일에서 @RequestMapping("todo") 를 사용했기 때문에 백앤드 주소는 http://localhost:8080/todo 이고, 이 주소에 위에서 만든 옵션을 백앤드 서버에 요청을 보낸다 (response.json() ⇒ json 형태로 요청을 의미)
  • 그리고 서버에서 반응(response) 가 오면 data를 Todo 아이템으로 저장하고, error 반응이 오면 그에 맞는 코드를 짜서 대응하면 된다.

위 코드 작성 후 컨솔창 접속시

해결방안 : CORS를 가능하게 하기 위해선 백엔드에서 CORS 방침 설정을 해줘야 한다.

  • 모든 경로(/**) 에 대해 Origin이 http://localhost:3000인 경우, GET,POST,PUT,PATCH,DELETE, OPTIONS 메서드를 이용한 요청을 허용한다.

  • 모든 헤더와 인증에 관한 정보도 허용한다.

  • 헤더에 해당 예비 요청이 브라우저에 캐시 될 수 있는 시간을 초 단위로 설정한다. (maxAge)

    문제 해결!

출처 : CORS1//CORS2

profile
방구석개발자

0개의 댓글