CORS 개념 정리

Big Jay·2022년 8월 22일
0

네트워크

목록 보기
1/2

CORS 오류 발생

실제로 혼자 동영상 사이트를 만들나 나타난 에러

client는 3000번 port를 쓰고 server는 4000번을 사용하고 있다. 위 오류는 `http://localhost:4000`에서 `http://localhost:3000`으로 가져올 수 있는 액세스가 CORS 정책에 의해 차단된 내용이다.

CORS란?

교차 출처 리소스 공유(Corss-Origin Resource Sharing, CORS)는 추가 HTTP헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제다. - mdn

CORS(Corss-Origin Resource Sharing)는 다른 도메인의 리소스가 안전한지 검사는 관문이다.

이러한 현상은 HTTP 요청에 대해서 어떤 요청을 하느냐에 따라 각기 다른 특징을 가지고 있기 때문에 발생한다.

HTML

기본적으로 Cross-Origin 정책을 따른다.

  • link 태그의 href에서 다른 origin의 css 등의 리소스에 접근하는 것이 가능
  • img 태그의 src에서 다른 리소스에 접근하는 것이 가능

XMLHttpRequest, Fetch API 등 script 태그 내

기본적으로 Same-Origin 정책을 따른다.

  • 자바스크립트는 서로 다른 도메인에 대한 요청을 보안상 제한한다. (브라우저 기본 설정은 하나의 서버 연결만 허용)
  • 이 정책을 Same-Origin-Policy라고 한다.

출처(Origin)

Protocol과 Host, Port까지 합친 것을 의미한다. Origin은 아래와 같이 구성되어 있다.

Origin 구조

자바스크립트로 Window(Location) 객체가 가지고 있는 origin 프로퍼티에 접근하여 출처를 알아낼 수 있다.

console.log(window.origin) //http://localhost:3000

Same Origin / Cross Origin 차이

웹에는 크게 SOP(Same Origin Policy)와 CORS(Cross Origin Resurce Sharing) 두가지 정책이 있다.

SOP - 동일 출처 정책

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

Same Origin 기준(출처 비교)

출처를 비교하는 방법은 URL의 구성 중 Protocol, Host, Port 세 가지가 동일한지 확인하면 된다. 일치하지 않는다면 CORS 요청을 실행한다.

출처 비교는 어떻게 되는가?
이는 브라우저에 구현된 스펙으로 브라우저가 판단한다. 만약 CORS정책을 위반하는 요청에 서버가 정상적으로 응답을 하더라고 브라우저가 이 응답을 분석해서 CORS정책에 위반되면 그 응답은 처리하지 않는다.

CORS

CORS오류가 생긴다면 COR를 허용하여 다른 출처의 리소스를 사용할 수 있게 하라는 내용이다.

CORS 기본 동작 과정

1. 클라이언트에서 HTTP요청의 헤더에 Origin을 담아 전달한다.

다른 Origin의 리소스 요청시 클라이언트는 HTTP요청을 보내는데, 이 때 요청헤더의 Origin필드에는 요청을 보내는 Origin을 담아 보낸다.

2. 서버는 응답헤더에 Access-Control-Allow-Origin을 담아 클라이언트로 전달한다.

서버가 요청에 대한 응답을 보낼때, 허락한 Origin을 클라이언트에게 전달한다.

3. 클라이언트에서, 자신이 보냈던 요청의 Origin과 서버가 보내준 Access-Control-Allow-Origin을 비교한다.

처음 보낸 Origin과 서버에서 전달받은 Access-Control-Allow-Origin을 비교하여 차단 여부를 결정한다.

❗️서버 응답은 CORS정책 위반 여부에 관여하지 않는다.
CORS 정책을 확인은 브라우저가 처리한다고 하였다.
브라우저가 CORS정책 위반을 분석하는 시간은 서버의 응답이 도착한 이후이다. 즉, CORS정책을 위반하는 리소스 요청때문에 에러가 발생하더라도 서버의 로그에서는 별 문제없이 응답했다라고 나온다.

CORS 작동 방식 -3가지 시나리오

실제 CORS가 동작하는 방식은 3가지 시나리오에 따라 변경된다.

예비요청(Preflight Request)

브라우저는 요청을 한번에 보내지 않고, 예비요청과 본요청으로 나누어 서버에 전달한다. 이 때 브라우저가 예비요청을 보내는 것을 Preflight라고 부르며 이 예비요청의 메소드 GET이나 POST가 아닌 OPTIONS라는 요청이 사용된다.
예비요청은 본 요청을 보내기 전에 브라우저 스스로 안전한지 확인하는 것이다.

  1. 자바스크립트에서 fetch()로 서버에 요청한다.
  2. 브라우저는 자바스크립트 요청에 의해 서버에 예비 요청으로 Orign을 전달한다. 이 때는 Get이나 Post가 아닌 Option이라는 독립적인 요청 메서드로 보내진다.
  3. 서버는 브라우저가 보낸 Origin을 받고 서버에서 정한 정책을 브라우저에 전달한다. ("Access-Control-Allow-Origin" : "*")
  4. 브라우저는 Origin과 서버에서 받은 정책을 확인 한 후 본 요청을 진행한다. 이 때는 Option이 아닌 목적에 맞는 요청을 보낸다.
  5. 브라우저는 본 요청에 의해 전달 받은 서버의 리소스를 자바스크립트에 전달한다.

예비 요청은 보통 PUT, DELETE 같은 요청을 보낼 때 이용된다. PUT이나 DELETE는 서버의 데이터를 변경해 버리는 요청이기 때문에, 코드가 돌아가게 하는 요청을 보내버리기전에 예비 요청을 보내서 우선 인증부터하고 본 요청을 받아 서버에서 코드가 돌아게하는 원리다.

단순 요청(Simple Request)

예비 요청을 보내지 않고 바로 서버에 직행으로 본 요청을 보낸 후, 서버가 이에 대한 응답의 헤더에 Access-Control-Allow-Origin과 같은 값을 보내주면 브라우저가 CORS정책 위반여부를 검사한다.

단순 요청의 경우 3가지 경우를 만족해야 가능하다.

  1. 요청 메소드가 GET, HEAD, POST중 하나여야 한다.(PUT, DELETE는 무조건 예비 요청)

  2. user-agent가 자동으로 설정한 Header외에 수동으로 설정할 수 있는 Header는 Fetch 명세에서 CORS-safelisted request-header로 정의한 header만 사용할 수 있다.

  3. Content-Type을 사용하는 경우에는 다음의 값들만 허용된다.

    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

위 조건과 같이 다소 까다로운 조건들이 많기 때문에 단순 요청이 일어나느 상황을 만드는 것이 쉽지 않다.
그 이유는 POST 요청이라고 해도 대부분 application/json으로 통신하기 때문에 3번 Content-Type이 위반된다. 따라서 대부분의 요청은 그냥 예비 요청으로 이루어진다.라고 이해하면 편하다.

인증된 요청(Credentialed Request)

기존 예비요청에서 보안을 더 강화하고 싶을 때 사용한다.
브라우저가 제공하는 비동기 리소스 요청 API인 XMLHttopRequest 객체나 fetch API는 별도의 옵션 없이 브라우저의 쿠키 정보나 인증과 관련된 헤더를 함부로 요청에 담지 않는다. 이ㄷ 때 요청에 인증과 쿠키를 담을 수 있게 해주는 옵션이 바로 credentials 옵션이다.

Credentials의 3가지 값

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

Cenenials 옵션값인 same-origin이나 include와 같은 옵션을 사용하여 리소스 요청에 인증 정보가 포함된다면, 브라우저는 다른 출처의 리소스를 요청할 때 Access-Control-Allow-Origin만 확인하는 것이 아니라 다른 조건을 검사한다.

  1. Access-Control-Allow-Origin에는 모든 요청을 허용하는 *을 사용할 수 없으며, URL을 명시해야한다.
  2. 응답 헤더에는 반드시 Access-Control-Allow-Credentials: true가 존재해야 한다.

인증된 요청(Credentialed Request) 역시 예비 요청처럼 Preflight가 먼지 일어난다.

CORS 해결 방법

1. 서버에서 Access-Control-Allow-Origin 세팅

가장 정석적이고 근본적인 해결책

res.setHeader("Access-Control-Allow-Origin","*")
res.setHeader("Access-Control-Allow-Credentials","true")// 쿠키 주고받기 허용

여기서 Origin에서 오는 요청을 "*"보다는 직접 명시해주는 것이 좋다.

res.setHeader("Access-Control-Allow-Origin","http://localhost:3000")

2. express cors 미들웨어 사용

express에서 제공하는 cors 미들웨어를 사용하여 해결할 수 있다.

현재 진행중인 개인 프로젝트는 express로 서버를 구성했기 때문에 이 방법으로 해결했다.

npm i cors
const cors = require("cors")

const corsConfig = {
origin: "http://localhost:3000",
  credentials: true
}

app.use(cors(corsConfig))

- 참고: express cors

참고

profile
안녕하세요.

0개의 댓글