article thumbnail image
Published 2022. 6. 7. 20:02
반응형

1. CORS(Cross Origin Resource Sharing)란?

- 다른 출처 리소스 공유

- HTTP 요청은 기본적으로 Cross-Site HTTP Requests가 가능하다.
- 하지만 Cross-Site HTTP Requests는 Same Origin Policy를 적용받기 때문에 요청이 불가하다.

그럼 Same Origin Policy는 뭐야?

먼저 Origin에 대해서 알아보자.

URL 구성 요소

 

Origin(출처)은 Protocol, Host, Port 번호까지 모두 합친 것을 의미한다.

 

SOP (Same Origin Policy)?

  • SOP는 지난 2011년, RFC 6454에서 처음 등장한 보안 정책으로 말 그대로 “같은 출처에서만 리소스를 공유할 수 있다”라는 규칙을 가진 정책이다.
  • 그러나 웹이라는 오픈스페이스 환경에서 다른 출처에 있는 리소스를 가져와서 사용하는 일은 굉장히 흔한 일이라 무작정 막을 수도 없는 노릇이니 몇 가지 예외 조항을 두고 이 조항에 해당하는 리소스 요청은 출처가 다르더라도 허용하기로 했는데, 그중 하나가 “CORS 정책을 지킨 리소스 요청”이다.

다른 출처로 리소스를 요청한다면 SOP 정책을 위반한 것이 되고, 거기다가 SOP의 예외 조항인 CORS 정책까지 지키지 않는다면 아예 다른 출처의 리소스를 사용할 수 없게 되는 것이다.

 

2. CORS 요청

  2.1 Simple Request (단순 요청)

  

예비 요청을 보내지 않고 바로 서버에게 본 요청부터 보낸 후 이에 대한 서버의 응답이 헤더에 Access-Control-Allow-Origin과 같은 값을 보내주면 그때 브라우저가 CORS 정책 위반 여부를 검사하는 방식. 2-2 Preflight Request와 Simple Request는 전반적인 로직 자체는 같으나, 예비 요청의 존재 유무만 다르다.

Simple Request

단순 요청의 조건
 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)만 허용한다.

1번 조건의 경우에는 put이나 delete와 같은 메서드를 사용하지 않으면 되는 것뿐이니 드문 상황이 아니지만 2번, 3번 같은 경우에는 조금 까다롭다. 저 조건에 명시된 헤더들은 진짜 기본적인 헤더들이기 때문에, 상용 웹 애플리케이션에서 이 헤더들 외에 추가적인 헤더를 사용하지 않는 경우는 매우 드물다. 사용자 인증에 사용되는 Authorization 헤더조차 포함되지 않는다.

 

게다가 대부분의 HTTP API는 text/xml이나 application/json 콘텐츠 타입을 가지도록 설계되어 있어서 이 조건들을 모두 만족하는 상황은 쉽지 않다.

 

   2.2 Preflight Request

 

 프리플라이트(Preflight) 방식은 브라우저가 요청을 한 번에 보내지 않고 예비 요청과

본 요청으로 나누어서 서버로 전송하는 방식을 말한다. 이때 본 요청을 보내기 전에 보내는 예비 요청을

Preflight라고 부른다. 이 예비 요청에는 HTTP 메서드 중 OPTIONS 메서드가 사용된다. 예비 요청은

본 요청을 보내기 전에 브라우저 스스로 요청을 보내는 것이 안전한지 확인하는 것이다.

 

본 요청을 보내기 전에 예비 요청을 먼저 보내고, 유효성을 검사한다.

javascript를 통해 브라우저에게 리소스를 받아오라고 명령을 내리면 브라우저는 서버에게 예비 요청을 보낸다.

이 예비 요청에 대한 응답으로 현재 어떤 것을 허용하고, 어떤 것들은 허용하지 않는지에 대한 응답 정보를 헤더에 담아서 브라우저에 다시 보내준다.

 

이후 브라우저는 예비 요청과 서버가 응답에 담아준 허용 정책을 비교한 후, 안전하다고 판단되면 같은 엔드포인트로 다시 본 요청을 보낸다. 이후 서버가 이 본 요청에 대한 응답을 하면 최종적으로 응답 데이터를 자바스크립트에 넘겨준다.

 

  2.3 Credentialed Request

인증된 요청을 사용하는 방법이다. 이 방법은 CORS의 기본적인 방식이라기 보다는 다른 출저 같 통신에서 좀 더 보안을 강화하고 싶을 때 사용하는 방법이다.

 

기본적으로 브라우저가 제공하는 비동기 리소스 요청 API인 XMLHttpRequest 객체나 fetch API는 별도의 옵션 없이 브라우저의 쿠키 정보나 인증과 관련된 헤더를 함부로 요청에 담지 않는다. 이때 요청에 인증과 관련된 정보를 담을 수 있게 해주는 옵션이 바로 credentials 옵션이다. 이 옵션은 총 3가지 값을 사용할 수 있다.

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

same-origin 이나 include와 같은 옵션을 사용하여 리소스 요청에 인증 정보가 포함된다면 다른 출처의 리소스를 요청할 때 단순히 Access-Control-Allow-Origin만 확인하는 것이 아니라 좀 더 강화된 검사 조건을 추가한다.

 

  • CORS 정책 위반 여부를 검사하는 룰
1. Access-Control-Allow-Origin에는 *을 사용할 수 없으며, 명시적인 URL이어야한다.
2. 응답 헤더에는 반드시 Access-Control-Allow-Credentials: true 가 존재해야한다.

 

3.CORS를 해결할 수 있는 방법

  3.1 Access-Control-Allow-Origin 세팅하기

CORS 정책 위반으로 인한 대표적인 방법은 서버에서 Access-Control-Allow-Origin 헤더에 알맞은 값을 세팅해주는 것이다.

이때 와이드카드인 *을 사용하여 헤더를 세팅하게 되면 모든 출처에서 오는 요청까지 받으므로 보안적으로 심각한 이슈가 발생할 수 있다.

가귀찮더라도 Access-Control-Allow-Origin: https://roobi-story.tistory.com 과 같이 출처를 명시한다.

Nginx나 Apache와 같은 서버 엔진의 설정에서 추가할 수도 있지만 복잡한 세팅을 하기에는 불편하기 때문에 소스 코드 내에서 응답 미들웨어 등을 사용하여 세팅하는 것을 추천한다. Spring, Express, Django와 같이 이름있는 백엔드 프레임워크의 경우에는 모든 CORS 관련 설정을 위한 세팅이나 미들웨어 라이브러리를 제공하고 있으니 세팅 자체가 어렵지 않다.

 

  3.2 Webpack Dev Server로 리버스 프록싱하기

CORS를 가장 많이 마주치는 환경은 로컬에서 프론트엔드 어플리케이션을 개발하는 경우이다. 백엔드에는 이미 Access-Control-Allpw-Origin 헤더가 세팅되어있겠지만, 이 중요한 헤더에다가 http://localhost:3000 같은 범용적인 출처를 넣어주는 경우는 드물기 때문이다.

 

프론트엔드 개발자는 대부분 웹팩과 webpack-dev-server를 사용하여 자신의 머신에 개발 환경을 구축하게 되는데 이 라이브러리가 제공하는 프록시 기능을 사용하면 편하게 CORS 정책을 우회할 수 있다.

module.exports = {
	devServer: {
    	proxy: {
        	'/api': {
            	target: 'https://roobi-story.tistory.com',
                changeOrigin: true,
                pathRewrite: { '^/api': '' },
               },
          }
     }
}

이렇게 설정을 해놓으면 로컬 환경에서 /api로 시작하는 URL로 보내는 요청에 대해 브라우저는 localhost:8000/api로 요청을 보낸 것으로 알고 있지만, 실질적으로는 웹팩이 https://roobi-story.tistory.com으로 요청을 프록싱해주기 때문에 마치 CORS 정책을 지킨 것처럼 브라우저를 속이면서 원하는 서버와 자유롭게 통신을 할 수 있다. 즉 프록싱을 통해 CORS 정책을 우회하는 것이다.

 

webpack-dev-middleware와 Node서버의 조합으로 개발 환경을 구축했더라도 http-proxy-middleware 라이브러리를 사용하면 손쉽게 프록시 설정을 할 수 있다. (webpack-dev-server도 내부적으로는 http-proxy-middleware를 사용한다.)

다만 이 방법은 실제 프로덕션 환경에서도 클라이언트 어플리케이션의 소스를 서빙하는 출처와 API 서버의 출처가 같은 경우에 사용하는 것이 좋다. 로컬 개발 환경에서는 웹팩이 요청을 프록싱해주니 아무 이상이 없겠지만, 어플리케이션을 빌드하고 서버에 올리고 나면 더 이상 webpack-dev-server가 구동하는 환경이 아니기 때문에 이상한 곳으로 API 요청을 보낼 수 있다.

 

예를 들어 API 서버의 출처는 https://api.roobi.com이고 클라이언트 어플리케이션을 서빙하는 서버의 출처는 https://www.roobi.com이라면 다음과 같은 상황이 발생한다. 

 

fetch('/api/me');
로컬환경에서
GET https://api.roobi.com/me 200 OK

실제 서버에는 프록싱 로직이 없다
GET https://www.roobi.com/api/me 404 Not Found

비즈니스 로직 내에서 process.env.NODE_ENV와 같은 빌드 환경 변수를 사용하여 분기 로직을 작성하는 방법도 있지만 비즈니스 로직에 이런 개발 환경 전용 소스가 포함되는 것은 추천하지 않는다.

 

참고사이트

https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy

 

동일 출처 정책 - 웹 보안 | MDN

동일 출처 정책(same-origin policy)은 어떤 출처에서 불러온 문서나 스크립트가 다른 출처에서 가져온 리소스와 상호작용하는 것을 제한하는 중요한 보안 방식입니다. 동일 출처 정책은 잠재적으로

developer.mozilla.org

https://developer.mozilla.org/ko/docs/Web/HTTP/CORS

 

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

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

developer.mozilla.org

 

원문 참고

https://evan-moon.github.io/2020/05/21/about-cors/

 

CORS는 왜 이렇게 우리를 힘들게 하는걸까?

이번 포스팅에서는 웹 개발자라면 한번쯤은 얻어맞아 봤을 법한 정책에 대한 이야기를 해보려고 한다. 사실 웹 개발을 하다보면 CORS 정책 위반으로 인해 에러가 발생하는 상황은 굉장히 흔해서

evan-moon.github.io

반응형

'programming > HTML, CSS' 카테고리의 다른 글

CSS 기본적으로 적용하기  (0) 2022.05.12
html 자주 사용하는 태그들  (0) 2022.05.12
[tip] 아이디어를 떠올리기 좋은 사이트  (0) 2021.09.14
[html] 반응형 html 페이지  (0) 2021.09.07
[html] html 기본  (0) 2021.08.21
복사했습니다!