SOP 및 Cors의 개념
HTTP 통신이 네트워크 망을 통해 확산되고, 여러 리소스가 공유되기 시작하면서 보안 상의 취약점이 대두되었다.
이를 개선하고자 등장한 개념이 SOP와 Cors 개념이다.
모든 웹은 각각의 출처(Origin)를 갖는다.
출처는 프로토콜 + 호스트 + 포트의 조합으로 되어 있고, 이 중 하나라도 다르면 동일한 출처로 보지 않는다.
아래는 동일하지 않지만 헷갈리기 쉬운 출처의 예시이다.
1. 프로토콜 (http vs https) : http://example.com vs https://example.com
2. 호스트 (haseong.example vs example) : http://haseong.example.com vs https://example.com
3. 포트 (:443 vs :8080) : http://example.com:443 vs http://example.com:8080
SOP 정책 (Same-Origin Policy) : "동일 출처만이 리소스 공유가 가능하다"
SOP는 이러한 Origin을 기준으로 동일한 출처끼리만 리소스(미디어/스크립트 등)를 공유할 수 있는 "정책"을 의미한다.
하지만, HTTP로 통신을 하다보면 다른 도메인에 위치한 미디어 파일 및 스크립트를 가져와 사용해야 할 때가 있는데, 이 때 다른 출처끼리도 통신이 가능도록 정의해둔게 Cors 정책이다.
Cors 교차 출처 리소스 공유 (Cross-Origin Resource Sharing) : 다른 출처가 선택한 자원에 대한 접근이 가능하도록 권한 부여
이와 같은 개념이 가능하게 된 원리는 다음과 같다.
개념이 조금 복잡해보이지만, 실제로는 그냥 HTTP 요청에서 OPTION 메서드를 통해 선행 요청(preflight, 프리플라이트)을 보낸다.
이후 해당 요청이 승인되면 실제 데이터를 요청하는 것이다.
1. 일반적인 접근 요청 (Simple Requset)
import axios from 'axios';
const API_URL = 'https://example.com';
axios.get(`${API_URL}/api/data`).then((response) => {
console.log(response.data);
}).catch((error) => {
console.error(error);
});
2. 인증정보를 포함하는 요청 (Credential Requset)
import axios from 'axios';
const API_URL = 'https://example.com';
axios.get(`${API_URL}/api/data`, {
withCredentials: true, // 추가
}).then((response) => {
console.log(response.data);
}).catch((error) => {
console.error(error);
});
사실, 프론트엔드 측에서는 Cors에러 자체를 제어할 수 있는 방법은 그다지 많지 않다.
(애초에 클라이언트 요청에서 발생되는 보안이슈를 다루기 위해 등장한 개념이니 당연한 걸지도;;;)
그러나 프론트엔드 개발 과정에서 발생하는 Cors 에러는 상당히 난감할 수 있다. 제한된 시간과 자원을 갖고 개발을 진행해야 하는데, 백엔드 측에서 Cors 에러를 해결해 줄 때까지 기다리기 어려울 수 있기 때문이다.
이에, 프론트엔드 측에서 "권장되지는 않지만~" Proxy 서버를 두고 이 서버를 통해 Cors 에러를 우회할 수도 있다.
React.js에서 빌드시에 사용할 수 있는 Webpack dev server proxy 개념을 통해 프론트엔드 개발용 proxy 서버를 설정할 수 있다.
Proxy Server
프록시 서버는 백엔드 보안 정책으로 인해 발생하는 오류를 우회하기 위해 프론트엔드 측에서 설정하는 중계서버이다.
기존에는 실제 서버와 직접 http 메서드를 활용하여 통신했지만, proxy 서버를 활용하게 되면 "클라이언트 - 브라우저 - proxy - 백엔드" r구조로 통신을 주고받게 된다.
다음은 간단한 proxy 설정 예제이다.
먼저 package.json에서 자동으로 전역적인 proxy 서버를 설정하는 방법이다.
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^16.8.0",
"react-dom": "^16.8.0",
"react-scripts": "3.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"proxy": "http://localhost:3001" // 이 부분을 추가!
}
이후 npm install로 패키지를 설치한 뒤, react project에서 해당 url로 서버통신을 시도한다면, 자동으로 proxy 서버를 통해 통신할 수 있게 된다.
import axios from 'axios';
const API_URL = '/api';
axios.get(`/data`).then((response) => {
console.log(response.data);
}).catch((error) => {
console.error(error);
});
수동으로 직접 파일 내부에 proxy 서버를 설정해주는 방법도 있다.
먼저, bash에서 패키지 매니저를 활용하여 http-proxy-middleware 라이브러를 설치한다.
npm install http-proxy-middleware --save
이후, react 프로젝트 내부에서 미들웨어를 호출한 뒤 다음과 같은 코드로 작성한다.
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api', // proxy가 필요한 path parameter를 입력한다.
createProxyMiddleware({
target: 'http://localhost:5000', // 타겟이 되는 api url를 입력한다.
changeOrigin: true, // 대상 서버 구성에 따라 호스트 헤더가 변경되도록 설정하는 부분이다.
})
);
};
// 또한 프록시 서버를 활용하여 우회할 경우, 반드시 기존의 axios 혹은 fetch로 받아온 url을 제거해야 한다.
'Network > HTTP' 카테고리의 다른 글
Cookie & Session (0) | 2023.03.07 |
---|---|
HTTP Message & Method (0) | 2023.03.06 |
HTTP 개념 : URL, URI (0) | 2023.03.02 |