[ 2023/03 ] 2022/06에 작성한 내용을 보충하여 재발행합니다.
✈ CORS
아마추어 개발자들끼리 프로젝트를 진행하면 늘 한 번씩 CORS Error 를 보곤한다
원활한 문제 해결을 위해 이에 대한 개념을 더 살펴보기로 하자.
SOP (Same-Origin Policy)
웹에서 돌아가는 어플리케이션은 사용자의 공격에 너어어어무 취약하다.
당장 DOM이 어떻게 되어있는지, 어떤 서버와 통신하는지, 리소스의 origin까지 다 알 수가 있다.
이에 대한 제한이 필요하였다.
SOP는 "같은 출처에서만 리소스를 공유할 수 있다"라는 규칙을 가진 정책이다.
즉, 다른 출처의 리소스를 사용하는 것에 제한하는 보안 방식이다. (http ~ https 가 다르다거나 등등..)
그러나 웹상에서 다른 출처에 있는 리소스를 가져와서 사용하는 일은 굉장히 흔하기 때문에, 몇 가지 예외 조항을 두게 되는데, 그 중 하나가 "CORS 정책을 지킨 리소스 요청"이다.
출처란?
:: protocol(Header scheme), host(Header domain), port(Header port) 가 같아야 같은 출처, 하나라도 다르면 다른 출처로 인식한다
CORS (Cross-Origin Resource Sharing)
:: 다른 출처의 자원을 공유할 수 있는 것으로, SOP 에서 허용하는 출처를 설정해준다
이는 교차 출처 리소스 공유로서, 추가 HTTP 헤더를 사용해 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다
- MOZILLA
이 CORS는 대-체 어떻게 동작하는지를 살펴보자.
HTTP 프로토콜을 사용해 Request를 보낼 때에, 브라우저는 요청 헤더에 Origin 이라는 필드에 요청을 보내는 출처를 함께 담아보낸다.
이후 서버 측에서 Response를 할 때에, Access-Control-Allow-Origin 이라는 값에 "리소스를 접근하는 것이 허용된 출처"를 내려주고,
이를 받은 브라우저는 본인이 보낸 Origin과 비교를 하여 유효한 응답인지를 결정한다.
CORS의 시나리오로는 1) Preflight / 2) Simple / 3) Credentialed Request 가 있다.
- 1) Preflight request
브라우저가 본 요청을 보내기 전에 Preflight라고 부르는 예비 요청을 보내고, 이 Request에는 OPTION 메소드가 사용된다.
서버는 이 예비 요청에 대한 Response로 허용하는/금지하는 정보를 응답 헤더에 담아 보내준다.
이후 브라우저는 Request와 Response의 허용 정책을 비교한 후에, 안전하다고 판단되면 같은 end-point로 다시 본 요청을 보내게 된다.
이 과정을 통해 브라우저 스스로 이 요청을 보내는 것이 안전한지를 확인한다.
(이 때, 예비 요청 통신의 200 상태 코드 여부와, CORS 에러와는 독립적이다. CORS 정책 위반 여부는 Origin의 일치여부만을 판단한다.)
- 2) 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 등 DB 상에 상이한 로직이 발생할 수 있기 때문이다.
그 이외에 조건은 만족시키기에 까다로워, 경험하기 쉽지는 않다.
- 3) Credentialed Request
Credentialed Request는 인증 관련 헤더를 포함할 때 사용하는 요청으로, JWT 인증 토큰을 주고받을 때 등에 사용될 수 있다.
기본적으로 브라우저가 제공하는 비동기 리소스 요청 API인 XMLHttpRequest 객체나 fetch API 에서 하지 않는, 요청에 인증과 관련된 정보를 담을 수 있게 해주는 옵션이 credentials 옵션이다.
클라이언트 측에서 credentials: include 를, 서버 측에서 Access-Control-Allow-Credentials: true 를 추가해주어야 한다.
브라우저는 단순히 Access-Control-Allow-Origin 만 확인하지 않고, 다음과 같은 룰이 추가된다.
1. Access-Control-Allow-Origin 에는 *를 사용할 수 없으며, 명시적인 URL이어야한다.
2. 응답 헤더에는 반드시 Access-Control-Allow-Credentials: true 가 존재해야한다.
✈ Proxy
CORS 를 해결하기 위해서는 Origin Allow 속성에 클라이언트에서 보내는 출처를 추가해주어야 한다
또한 배포하는 AWS 같은 곳에서도 따로 설정을 해주어야 한다고 한다
하지만 서버 측에서 이러한 해결을 해주지 못할 경우,
클라이언트 측에서는 프록시 서버를 설정하여 해결할 수 있다
프록시 서버 우회는 개발 환경에서만 가능하다고 하니 주의하자 !
프록시 서버 설정 방법 3가지는 다음과 같다
1. `react-script` 이용
`package.json` 다음과 같이 proxy 프로퍼티를 추가한다
"proxy": "http://{서버 엔드포인트 주소}",
하고 요청을 할 때는 엔드포인트 주소를 빼고 작성한다
2. `heroku` 의 우회 서버 이용
http://cors-anywhere.herokuapp.com/
위의 사이트에 들어가서 데모서버 켜기 누르고,
엔드 포인트를
http://cors-anywhere.herokuapp.com/http://{서버 엔드포인트 주소}
와 같이 작성하여 사용한다
3. `http-proxy-middleware` 이용
사실 라이브러리 설치할 필요 없이 위 2가지 방법 사용하면 되는데,
웹 소켓 통신을 할 시에 잘 활용 된다
https://create-react-app.dev/docs/proxying-api-requests-in-development/
위 공식문서에 매뉴얼이 잘 나와있으니 따라서 하면 된다
yarn add http-proxy-middleware
설치하고, src/setupProxy.js 에 다음과 같이 추가한다
const createProxyMiddleware = require("http-proxy-middleware");
module.exports = app => {
app.use(
createProxyMiddleware(
"/review",
{
target: "http://{서버 엔드포인트 주소}",
changeOrigin: true,
// ws: true,
// router: {
// "/socket.io": "ws://{서버 엔드포인트 주소}"
// }
}
)
)
}
후에 브라우저 상에서의 방화벽 문제가 있다면, 아래 설정에서 윈도우 설정을 추가적으로 해준다
- Evan Library :: https://evan-moon.github.io/2020/05/21/about-cors/
---
- 나봄의 CORS :: https://www.youtube.com/watch?v=-2TgkKYmJt4&t=1071s
- 얄코의 코딩사전 :: https://www.youtube.com/watch?v=bW31xiNB8Nc
'DevOps' 카테고리의 다른 글
[Bundler] The meaning of migration to Vite (5) | 2023.04.15 |
---|---|
[E2E Test][Cypress] Cypress로 E2E 테스트해보기 (0) | 2022.05.18 |
[AWS] 3-Tier Web Application Architecture 구성 프로젝트 (0) | 2021.09.19 |