[TIL] CORS

XCC629·2022년 6월 13일
0

frontend

목록 보기
7/16
post-thumbnail

Cross-Origin Resource Sharing (CORS)

정의: 다른 출처에게 요청하는 것이 안전한지 판별하기 위해서 브라우저와 상호 통신하는 하나의 방법

즉, 웹 애플리케이션이 지금 실행중인 출처와 다른 출처의 자원에 접근할 수 있도록 권한을 부여하도록 브라우저에게 알려주는 체제

요약: 출처 다른 애플리케이션에 아무나 접근하지 못하도록 방지하는 방어막. 허락해줘야지 리소스 쓸 수 있다.


또다른 정책 : SOP(Same-origin Policy)

같은 출처에서만 리소스를 공유할 수 있다 라는 정책

웹의 특성(누구나 쓸수있음)을 고려하여 예외 사항을 만듦

  • cors 정책을 지킨 리소스 요청

결론: 다른 출처로 리소스 요청하면 SOP 정책 위반함. 더해서, 예외 사항인 CORS 정책까지 안지키면 다른 출처 리소스 사용 불가! (CORS만 통과하면 쓸 수있다.)


출처

출처: Protocol + Host + 포트번호(있다면)

출처 비교: 브라우저가 함

  • 서버는 누구나 요청하면 다 주는데, 브라우저에서 출처가 다르면 응답을 버림.
  • 해석이 애매하면 브라우저가 독자적으로 판단. 문제는 브라우저마다 판단이 다름.

동작 방법

기본흐름

클라이언트: 다른(교차) 출처 리소스 요청시에는 HTTP 프로토콜을 사용해서 요청.

👇

브라우저

  • 이때에 Origin(출처)라는 필드에 출처 담아서 보냄

👇

서버

  • 응답 시, 응답 헤더 Access-Control-Allow-Origin에 접근 허용된 출처 내려줌

👇

브라우저

  • 보낸 Origin과 응답 헤더의 Access-Control-Allow-Origin가 같은 지 비교

그러나.... 실제 시나리오는 3개다.

  • Preflight Request(프리플라이트)
  • Simple Request
  • Credentialed Request

1. Preflight Request

  • 일반적인 시나리오
  • 이 시나리오에서는?
    브라우저: 요청을 한 번에 보내지 않고, 예비(Preflight)와 본 요청을 나누어 서버에 전송.

예비요청: HTTP 메소드 중 OPTIONS 메소드 사용

  • 본 요청 전에 브라우저가 안전한지 확인하기 위해서 보낸다.
  • 응답: 현재 허용하는 것과 금지하는 것에 대한 정보를 헤더에 담아서 보내준다.
  • 예비 요청의 성공과 cors에러는 관계없다. (예비요청 => cors 판단이라서)

실제 예시
네트워크 탭 열어서 확인가능

요청헤더

accept: */*
accept-encoding: gzip, deflate, br
accept-language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
access-control-request-headers: content-type
access-control-request-method: GET
cache-control: no-cache
origin: https://www.naver.com
pragma: no-cache
referer: https://www.naver.com/
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: same-site
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.0.0 Safari/537.36

access-control-request-headers: content-type
access-control-request-method: GET
origin: https://www.naver.com
이런 정보들을 다 보낸다.

응답헤더

access-control-allow-credentials: true
access-control-allow-headers: content-type
access-control-allow-methods: GET
access-control-allow-origin: https://www.naver.com
access-control-max-age: 1800
allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
content-length: 0
date: Mon, 13 Jun 2022 05:52:51 GMT
referrer-policy: unsafe-url
server: nfront
vary: Origin
vary: Access-Control-Request-Method
vary: Access-Control-Request-Headers

access-control-allow-origin: https://www.naver.com
보낸 출처와 같으니 잘 작동한다. 만약 달랐다면 cors 에러.


2. Simple Request

  • 프리플라이트와 같은 로직에 예비요청 없음
  • 일단 요청 보내놓고 응답 헤더에 있는 Access-Control-Allow-Origin로 판단
  • 까다로운 조건에서만 사용 가능. (사실 상 현실에선 불가능?)

까다로운 조건...
1. 요청의 메소드는 GET, HEAD, POST 중 하나여야 한다.
2. Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width를 제외한 헤더를 사용하면 안된다.
3. 만약 Content-Type를 사용하는 경우에는 application/x-www-form-urlencoded, multipart/form-data, text/plain만 허용된다.


3. Credentialed Request

  • 인증된 요청 사용하는 방법
  • 다른 출처 간 통신에서 보완 강화하는 방법 (쿠키나 인증 같은 중요한 것들을 위해)
  • 사용방법: fetch 할때 옵션설정하면 됨.

옵션 종류

  • same-origin(기본값): 같은 출처 간 요청에만 인증 정보를 담을 수 있다.
  • include: 모든 요청에 인증 정보를 담을 수 있다.
  • omit: 모든 요청에 인증 정보를 담지 않는다.

옵션에 따라서 브라우저가 cors 정책 검사를 더 엄격하게 한다.

사용예시

fetch('https://어쩌구 주소...', {
  credentials: 'include',
});

이렇게 사용하면, 모든 요청에 쿠키랑 인증 등등 민감한 정보를 보내게 된다. Access-Control-Allow-Origin* 같은 값 안된다. 또, 응답 헤더에는 반드시 Access-Control-Allow-Credentials: true가 존재해야한다


CORS 해결방법

  1. 서버에서 Access-Control-Allow-Origin 세팅해주기 (* 말고 url로 해주는게 적절하다.)
  2. 프록시로 잠깐 브라우저 속이기

결론: 어차피 해야할 설정. 처음부터 서버에서 해주는 게 제일 편하고 정석이다.


참고자료

CORS는 왜 이렇게 우리를 힘들게 하는걸까?
[Node.js] express cors 사용하기

profile
프론트엔드 개발자

0개의 댓글