CORS가 뭔데 자꾸 뜨냐?

요들레이후·2023년 5월 23일
2

FE study

목록 보기
4/5

💡 Cross-Origin Resource Sharing

  • 브라우저는 모든 요청 시 Origin 헤더를 포함한다.
  • 서버는 Origin 헤더를 보고, 해당 요청이 원하는 도메인에서부터 출발한 것인지를 판단
  • 다른 Origin 에서 온 요청은 서버에서 기본적으로 거부함
  • 그러나 보통 서버의 endpoint와 홈페이지 domain은 다른 경우가 많음
  • 따라서 서버에서는 홈페이지 domain을 허용하여, 다른 domain이라 하더라도 요청을 보낼 수 있게 함
  • 서버는 Access-Control-Allow-Origin외에 Access-Control-*을 포함하는 헤더에 CORS 관련 정보를 클라이언트로 보냄
  • 웹사이트에 악성 script가 로드되어, 수상한 요청을 하는 것을 막기 위함
  • 반대로, 익명 유저로부터의 DDos공격 등을 막기 위함
  • 서버에 직접 CORS 설정을 할 수 없다면, Proxy 서버 등을 만들어 해결

Same-Origin Policy(SOP)

SOP는 처음 제안된 웹 브라우저 보안 정책이다. HTTP Request를 전송할 때 다른 Origin 으로의 전송을 금지한다. 즉, 같은 Origin 에서만 리소스를 공유할 수 있다는 규칙을 가진 정책이다.

이 정책을 적용하는 호스트는 CSRF(Cross-Site Request Forgery), XSS(Cross-Site Scripting)과 같은 공격으로부터 방어할 수 있다.

CSRF(Cross-Site Request Forgery) : 사이트 간 요청 위조
⇒ 정상적인 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위(수정, 삭제, 등록)를 특정 웹사이트에 요청하게 되어 서버를 공격하게 되는 경우이다.
https://junhyunny.github.io/information/security/spring-boot/spring-security/cross-site-reqeust-forgery/

XSS(Cross-Site Scripting) : 사이트 간 스크립팅
⇒ 웹사이트 관리자가 아닌 권한이 없는 사용자가 웹 페이지에 악성 스크립트를 삽입할 수 있는 취약점

Origin

Origin = Protocol + host, ex) https://www.naver.com:8080

Origin(출처)이란 URL에서 프로토콜(http, https 등), 도메인, 포트번호를 모두 합쳐 노드를 식별하는 개념이다.

네이버라는 브라우저를 접속한다고 했을 때, 브라우저를 통해서 접속을 한다. 그 브라우저에서는 네이버에서 만든 서버가 데이터를 줘서 보게 되는 것이다.

그런데 예를 들어서 악의적인 사용자가 나인척 하면서 하이재킹을 해서 정보를 빼갈 수도 있다. 그런 것을 막기 위해서 같은 Origin 끼리만 데이터를 송수신 하고자 하는 것이 SOP이다.

CORS 등장 배경

브라우저는 모든 요청 시 Origin 헤더를 포함한다. 서버는 Origin 헤더를 보고, 해당 요청이 원하는 도메인에서부터 출발한 것인지를 판단한다. 다른 Origin 에서 온 요청은 서버에서 기본적으로 거부한다. / sop

하지만 보통 서버의 endpoint와 홈페이지 domain은 다른 경우가 많다.

이전에는 동일한 도메인에서 리소스를 받아왔는데, 지금은 클라이언트에서 도메인이 다른 서버에서 제공하는 API를 사용하는 일이 많이졌다.

따라서 서버에서는 홈페이지 domain을 허용하여, 다른 domain이라 하더라도 요청을 보낼 수 있게 하는 정책이 생겼고, 그것이 바로 CORS이다.




CORS(Cross Origin Resource Sharing)

CORS는 한 도메인 또는 Origin의 웹 페이지가 다른 도메인 (도메인 간 요청)을 가진 리소스에 액세스 할 수 있게하는 보안 메커니즘이다.

CORS는 서버와 클라이언트가 정해진 헤더를 통해 서로 요청이나 응답에 반응할지 결정하는 방식으로 CORS라는 이름으로 표준화 되었다.

CORS Error

같은 Origin상에서는 요청이 오는 곳과 처리하는 곳이 동일하기 때문에 특별히 보안상 처리해줄 이유가 없다. 그런데 다른 Origin 에서 오는 요청이라면 내가 요청으로 받아온 결과를 믿을만한지 검증하는 과정이 필요하다.

브라우저에서는 결과의 헤더값을 통해 CORS를 확인한다. ‘Access-Control-Allow-Origin’, ‘Access-Control-Allow-Methods’가 브라우저에서 CORS를 검증할 때 사용하는 값이다.

브라우저에선 localhost:8080 서버에서 전달 받은 응답 중 헤더에 Access-Control-Allow-Origin 값을 확인하고 이 값에 현재 Origin이 포함되는지 확인한다. 포함되어 있다면 CORS를 수행하고 그렇지 않으면 에러를 낸다.

💡 브라우저 CORS기본 동작
1. 클라이언트에서 HTTP요청의 헤더에 Origin을 담아 전달
- 기본적으로 웹은 HTTP 프로토콜을 이용하여 서버에 요청 을 보내게 되는데,
- 이때 브라우저는 요청 헤더에 Origin 이라는 필드에 출처를 함께 담아 보내게 된다.

2. 서버는 응답헤더에 Access-Control-Allow-Origin을 담아 클라이언트로 전달한다.
- 이후 서버가 이 요청에 대한 응답을 할 때 응답 헤더에 Access-Control-Allow-Origin 이라는 필드를 추가하고 값으로 ' 이 리소스를 접근하는 것이 허용된 출처 url'을 내려보낸다.

3. 클라이언트에서 Origin과 서버가 보내준 Access-Control-Allow-Origin을 비교한다.
- 이후 응답을 받은 브라우저는 자신이 보냈던 요청의 Origin
과 서버가 보내준 응답의 Access-Control-Allow-Origin을 비교해본 후 차단할지 말지를 결정한다.
- 만약 유효하지 않다면 그 응답을 사용하지 않고 버린다. (CORS 에러 !!)
- 위의 경우에는 둘다 http://localhost:3000이기 때문에 유효하니 다른 출처의 리소스를 문제없이 가져오게 된다.

위 사진의 빨간 줄은 Origin에 포함되어 있지 않았기 때문에 브라우저에서 CORS를 허용하지 않아 발생한 것이다. Origin이 포함되어 있다면 Method도 확인하고 Content Type도 본다.

CORS에러는 브라우저에서 내뿜는 에러다.

그래서 postman과 같이 API를 테스트하는 도구에서 서버에 요청하면 문제없이 응답이 오는데, 프론트엔드 작업을 할 때만 CORS 에러가 발생하는 것도 브라우저가 미리 보고 개입했기 때문에 발생한다.

근데 우리가 욕했던 CORS는 사실 해결책이다.
시뻘건 에러 메시지는 사실 브라우저의 SOP 정책에 따라 다른 출처의 리소스를 차단하면서 발생된 에러이며, CORS는 다른 출처의 리소스를 얻기 위한 해결 방안이었다.




CORS Solution

Chrome 확장 프로그램 이용

Chrome에서는 CORS 문제를 해결하기 위한 확장 프로그램을 제공해준다. 아래 링크에서 'Allow CORS: Access-Control-Allow-Origin' 크롬 확장 프로그램을을 설치 해준다.

Allow CORS: Access-Control-Allow-Origin

그러면 브라우저 오른쪽 상단에서 확장 프로그램을 활성화 시킬 수 있다. 해당 프로그램을 활성화 시키게 되면, 로컬(localhost) 환경에서 API를 테스트 시, CORS 문제를 해결할 수 있다.

서버 테스트 환경에서 고민하지 않고 빠르게 CORS를 해결하는데 좋은 선택지일 것이다.

프록시 사이트 이용하기

프록시(Proxy)란 클라이언트와 서버 사이의 중계 대리점이라고 보면 된다.

브라우저에서 보낸 요청을 프론트엔드에서 받아서 대신 보내는 방법이다. 서버 간의 데이터 교환상에서는 CORS가 이뤄지지 않기 때문에 프론트엔드 서버에서 받은 정보를 브라우저에 업데이트 해주기만 하면 된다.

즉, 프론트에서 직접 서버에 리소스를 요청을 했더니 서버에서 따로 설정을 안해줘서 CORS 에러가 뜬다면, 모든 출처를 허용한 서버 대리점을 통해 요청을 하면 되는 것이다.

다만 현재 무료 프록시 서버 대여 서비스들은 모두 악용 사례 때문에 api 요청 횟수 제한을 두어 실전에서는 사용하기 무리이다. 따라서 테스트용이나 맛보기용으로 사용하되, 실전에서는 직접 프록시 서버를 구축하여 사용하여야 한다.

1) heroku 프록시 서버

  1. http://cors-anywhere.herokuapp.com/corsdemoVisit Website
  2. 위의 사이트로 가서 버튼을 누르고 데모 서버를 활성화 시킨다.
  3. 다만 시간 제한이 있기 때문에 일시적인 해결 방편 이다.

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 프록시 서버

  1. raravelVisit Website님이 만드신 서비스
  2. 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 프록시 서버

  1. https://cors.sh/Visit Website
  2. 실전에서 이용하려면 유료 결제가 필요하듯 하다. (free도 가능)
const url = 'https://google.com' // 이 부분을 이용하는 서버 URL로 변경

fetch(`https://proxy.cors.sh/${url}`)
    .then((response) => response.text())
    .then((data) => console.log(data));

서버에서 Access-Control-Allow-Origin 헤더 세팅하기

직접 서버에서 HTTP 헤더 설정을 통해 출처를 허용하게 설정하는 가장 정석적인 해결책이다.

서버의 종류도 노드 서버, 스프링 서버, 아파치 서버 등 여러가지가 있으니, 이에 대한 각각 해결책을 나열해본다.

각 서버의 문법에 맞게 위의 HTTP 헤더를 추가해 주면 된다.

참고

https://selfish-developer.com/entry/지긋지긋한-CORS-error-이제-제대로-공부해보자
https://opentutorials.org/module/6257/32185
https://inpa.tistory.com/entry/WEB-📚-CORS-💯-정리-해결-방법-👏
https://velog.io/@yunsungyang-omc/React-React-App에서-CORS-이슈-해결하기
https://www.youtube.com/watch?v=j2Q2Ev6CZzQ

profile
💩 잘㈛는건 ㅇr닌데, 포ブl㈛ズl 않을꺼○F. ㄴr는 별로 코딩을 잘㈛ズl는 않ズl口ざ, ㄱH발ㅈr를 포ブl㈛ズl 않을꺼○F. ュ 정도로 포ブl를 먼저 んı작ㅎŁ⊂ト면, ㅇr무것도 도전㈛ヱ 싶ズl 않을 꺼○F. 💩

0개의 댓글