티스토리 뷰

CORS

CORS은 Cross-Origin Resource Sharing의 약자로 교차 출처 리소스 공유이다.

CORS란 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다.

 

쉽게 얘기하자면, 동일한 출처가 아닌 다른 출처에서 데이터를 주고 받는 것을 허용하는 정책이다.

 

CORS의 반대 개념으로는 SOP(Same-Origin Policy) 동일 출처 정책이 있다.

 

SOP

어떤 출처에서 불러온 문서나 스크립트가 다른 출처에서 가져온 리소스와 상호작용하는 것을 제한하는 보안 방식이다.

즉, SOP는 웹 브라우저에서 보안을 강화하기 위하여 동일한 출처에서만 리소스를 주고 받도록 하는 정책이다.

 

CORS와 SOP에서 계속 언급되는 출처란 두 URL의 프로토콜, 포트, 호스트(도메인)가 모두 같은 경우를 동일한 출처라고 한다.

URL 구조: https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_URL

 

동일한 출처 예시 사진: https://ko.wikipedia.org/wiki/%EB%8F%99%EC%9D%BC-%EC%B6%9C%EC%B2%98_%EC%A0%95%EC%B1%85

 

이러한 동일 출처 정책이 있기 때문에 브라우저가 다른 출처에서 리소스를 받아오는 것을 제한한다.

 

하지만 개발을 하다보면 기능을 위해 다른 출처 간의 상호작용을 해야하는 케이스도 있고, 네이버 지도 API같이 다른 회사의 서버 API를 이용해야 하는 상황도 존재한다.

따라서 이런 예외 사항을 두기 위해 CORS 정책을 허용하는 리소스에 한해서 다른 출처라도 받아들인다는 뜻이다.

즉, CORS는 다른 출처의 리소스를 얻기 위한 해결방안이다.

 

CORS 동작방식

CORS 접근 제어 시나리오 (교차 출처 리소스 공유가 동작하는 방식)에는 크게 세가지가 있다.

 

1. 프리플라이트 요청 (Preflight Request)

실제 요청을 보내기 전에 예비 요청을 보내 해당 출처 리소스에 접근 권한이 있는지부터 확인하는 방식

사진 출처: https://developer.mozilla.org/ko/docs/Web/HTTP/CORS#access-control-expose-headers

위의 사진과 같이 OPTIONS method를 통해서 Main request 전에 Preflight request를 보내고, Response 헤더의 Access-Control-Allow-Origin으로 요청을 보낸 출처가 돌아오면 Main request를 보낸다. 만약 요청을 보낸 출처가 접근 권한이 없다면 CORS 에러를 띄우고, 요청은 전달되지 않는다.

 

응답을 좀 더 자세히 보자면

Access-Control-Allow-Origin: http://foo.example
- 응답을 보내준 다른 출처

Access-Control-Allow-Methods: POST, GET, OPTIONS
- 서버의 응답. POST와 GET이 리소스를 쿼리하는데 유용한 메서드라고 가르쳐줌.

Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
- "X-PINGOTHER, Content-Type"으로 전송하여 실제 요청에 헤더를 사용할 수 있음을 확인

Access-Control-Max-Age: 86400
- 응답을 캐시할 수 있는 시간(초)을 제공. *86400(24시간)

 

2. 단순 요청 (Simple Requests)

사진 출처 http://man.hubwiz.com/docset/HTTP.docset/Contents/Resources/Documents/developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS.html

단순요청은 Preflight Request와 다르게 요청을 보내면서 즉시 Cross Origin인지 확인하는데 다음 조건을 모두 충족해야한다.

  • 다음 중 하나의 메서드
    • GET
    • HEAD
    • POST
  • 헤더는 Accept, Accept-Language, Content-Language, Content-Type만 허용
  • Content-Type 헤더는 다음 값들만 허용
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

 

여기서 한가지 의문이 든다.

 

Preflight Request에서 두번씩 요청을 보내는데 그냥 Simple Request로만 하면 안되는건가?

왜 Preflight Request가 필요할까?

 

Without Preflight

결론은 CORS를 모르는 서버를 위해서다.

CORS설정이 없는 서버에 Simple Request로만 요청을 보내게 되면 CORS 관련해서 서버는 모르기 때문에 일단 요청받은걸 해결하고 응답을 뱉어준다.

 

하지만 서버에서는 CORS 설정이 없기 때문에 Allow-Origin이 없다. 그럼 그제서야 브라우저는 클라이언트에게 CORS 에러를 던져준다.

 

만약 단순한 GET요청이 아니라 DELETE나 PUT같은 중요한 요청을 보냈다면 아무것도 모르는 서버는 DB의 데이터를 지우거나 수정을 할 것이고, 이렇게 되면 큰 문제가 발생한다.

 

3. 인증정보를 포함한 요청

사진 출처 https://developer.mozilla.org/ko/docs/Web/HTTP/CORS#access-control-expose-headers

요청 헤더에 인증 정보를 담아 보내는 방식

출처가 다를때 별도의 설정을 하지 않으면 쿠키를 보낼 수 없는데, 이런 경우에는 프론트와 서버 양측 모두 CORS 설정이 필요하다.

  • 프론트 측에서 요청 헤더에 widthCredentials: true 를 설정한다.
  • 서버 측에서 응답 헤더에 Access-Control-Allow-Credentials: true 를 설정한다.

credentials의 옵션

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

예시 코드

// fetch 메서드
fetch("https://example.com:1234/users/login", {
    method: "POST",
    credentials: "include", // 클라이언트와 서버가 통신할때 쿠키와 같은 인증 정보 값을 공유하겠다는 설정
    body: JSON.stringify({
        userId: 1,
    }),
})

이렇게 설정해주면서 자격 증명을 하고 실제 요청을 수행할 수 있음을 나타내야한다.

 

주의할 점은 모든 출처를 허용한다는 뜻의 와일드카드(*)로 설정하면 에러가 발생한다.

인증 정보인 만큼 출처를 정확히 지정해줘야 한다.

 

CORS 동작방식 체험 사이트

위의 3가지 CORS 시나리오를 체험해 볼 수 있고 요청 형태에 따라 에러가 나거나 통과되기도 하는 예제를 볼 수 있다.

https://chuckchoiboi.github.io/cors-tutorial/

 

CORS Tutorial

 

chuckchoiboi.github.io

 

CORS 에러 해결방법

 

1. Chrome 확장 프로그램 이용

아래 링크에서 확장 프로그램을 설치하고 활성화 시키게 되면 로컬 환경에서 API를 테스트 시, CORS문제를 해결할 수 있다.

https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf

 

Allow CORS: Access-Control-Allow-Origin

Easily add (Access-Control-Allow-Origin: *) rule to the response header.

chrome.google.com

 

 

2. 프록시 사이트 이용

Proxy ??
Proxy 뜻

CORS를 해결하려면 서버에서 Origin Allow 속성에 클라이언트에서 보내는 출처를 추가해주어야한다. 하지만 이런 문제를 해결하지 못하면 클라이언트 측에서 프록시 서버를 설정하여 해결할 수 있다.

※ 주의할 점은 프록시 서버는 개발환경에서만 가능하다.

 

프록시 서버란 클라이언트가 자신을 거쳐 다른 네트워크에 접속할 수 있도록 중간에서 대리해주는 서버를 말한다.

 

 

프록시 서버의 사용 목적

사용 목적에는 여러가지가 있을 수 있는데 대표적으로 아래와 같은 것들이 있다.

 

1. 캐시 데이터 사용

프록시 서버 중 일부는 요청된 내용을 캐시를 사용해서 저장해둔다. 그럼 캐시에 저장되어 있는 내용에 대한 재요청은 서버에 따로 접속할 필요가 없어지기 때문에 전송시간과 외부 트래픽을 줄일 수 있다.

 

2. 보안 목적

프록시 서버가 중간에 경유하면 IP를 숨기는 것이 가능하다. 또한 프록시 서버를 방화벽으로 사용하기도 한다.

 

3. 접속 우회

간혹 접속이 제한되는 사이트가 있는데 우회에 사용할 서버 주소와 포트를 구한 후 설정해주면 설정해준 서버에서 접속한 것처럼 속일 수 있기 때문에 접속 제한을 우회할 수 있다.

 

 

프록시 서버 설정 방법

 

1. heroku 프록시 서버

http://cors-anywhere.herokuapp.com/corsdemo 

위 사이트 들어가서 데모 서버 활성화 버튼 클릭한다. 시간제한이 있어서 일시적인 해결 방법이다.

const url = 'https://google.com' // 이 부분을 이용하는 서버 URL로 변경
 
fetch(`https://cors-anywhere.herokuapp.com/${url}`)
    .then((response) => response.text())
    .then((data) => console.log(data));

 

2. cors proxy app 프록시 서버

axios 라이브러리 설치가 필요하다.

<script src='https://cdnjs.cloudflare.com/ajax/libs/axios/1.1.3/axios.min.js'></script>
<script>
    axios({
        url: 'https://cors-proxy.org/api/',
        method: 'get',
        headers: {
            'cors-proxy-url' : 'https://google.com/' // 이 부분을 이용하는 서버 URL로 변경
        },
    }).then((res) => {
        console.log(res.data);
    })
</script>

 

3. cors.sh 프록시 서버

https://cors.sh/ 

const url = 'https://google.com' // 이 부분을 이용하는 서버 URL로 변경
 
fetch(`https://proxy.cors.sh/${url}`)
    .then((response) => response.text())
    .then((data) => console.log(data));

출처

 

10분 테코톡 https://www.youtube.com/watch?v=-2TgkKYmJt4&t=1071s 

 

[WEB] 📚 악명 높은 CORS 개념 & 해결법 - 정리 끝판왕 👏

악명 높은 CORS 에러 메세지 웹 개발을 하다보면 반드시 마주치는 멍멍 같은 에러가 바로 CORS 이다. 웹 개발의 신입 신고식이라고 할 정도로, CORS는 누구나 한 번 정도는 겪게 된다고 해도 과언이

inpa.tistory.com

 

 

교차 출처 리소스 공유 (CORS) - HTTP | MDN

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

developer.mozilla.org

 

 

[Web] CORS 동작 방식과 해결 방법

교차 출처 리소스 공유(Cross-Origin Resource Sharing)에 대해 알아보자.

ingg.dev

 

[Network] CORS란? (feat. 보안,HTTP) (What is a CORS?)

CORS란? CORS는 Cross-Origin Resource Sharing의 약자로 직역하면 "교차 출처 리소스 공유" 이다. 좀 더 쉽게 말하면 동일한 출처가 아닌 다른 출처에서 데이터를 주고 받는 것을 허용하는 정책이다. 그렇다

fomaios.tistory.com