일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- 정처기기출
- 코딩테스트
- 알고리즘문제
- 프로그래머스코딩테스트
- CSS
- html
- 프로그래머스 레벨0
- js
- 코테공부
- 자바스크립트
- 코딩
- Redux-Toolkit
- 프로그래머스 코테
- 알고리즘스터디
- 알고리즘공부
- 코테스터디
- 프로그래머스 코딩테스트
- next.js
- 1일1코테
- 코테준비
- 정보처리기사
- 프로그래머스코테
- 프로그래머스 알고리즘
- 프로그래밍
- 개발자
- next.js 에러
- 프로그래머스 Lv.0
- mysql
- 프로그래머스
- 프로그래머스알고리즘
- Today
- Total
계발하는 개발자
[Network/웹 보안] SOP(동일 출처 정책)와 CORS(교차 출처 리소스 공유)란? 본문
[Network/웹 보안] SOP(동일 출처 정책)와 CORS(교차 출처 리소스 공유)란?
dev_genie 2023. 9. 7. 10:00CORS 에러를 잘 표현해주는 그림이 있어 가져와봤다.
위 그림에서처럼 CORS 에러는 흔히 프론트엔드에서 백엔드 서버로 API 요청을 보낼 때 발생하는 에러 중 하나다.
내 경우에도 이번에 MySQL 서버에 있는 데이터를 요청하다가 CORS 에러를 만난 적이 있다.
⬇️ 이전 포스팅 참고
[CORS 에러] Access to XMLHttpRequest at '주소A' from origin '주소B' has been blocked by CORS policy 해결
에러 아파치 서버랑 vue.js dev 서버를 따로 띄워놓고 개발하다 '주소B(vue.js 포트)'로부터 '주소A(아파치 포트)'에 데이터를 요청했을 때 아래와 같은 에러가 떴다. 원인 보안상 웹 애플리케이션은
ziszini.tistory.com
그렇다면, 우릴 이렇게 힘들게 하는 CORS 에러! 대체 뭘까?
CORS
Cross Origin Resource Sharing, 교차 출처 리소스 공유
추가적인 HTTP 헤더를 사용하여, 다른 출처의 자원(ex: 데이터, 이미지, 폰트 등)에 접근할 수 있는 권한을 부여하는 메커니즘을 말한다.
CORS라는 이름 뜻 그대로 교차 출처, 즉 다른 출처 자원의 공유를 가능하게 만든다.
흔히 'CORS 에러' 등으로 많이 불려져서 에러를 뿜는 나쁜놈!!!🤬 이라고 생각했는데,
사실은 우리를 도와주는 착한 녀석(?)이었다.(반.전ㅋㅋ)
그래서 이 CORS 에러를 잘 이해하기 위해선 덧붙여서 SOP(동일 출처 정책, Same Origin Policy) 라는 것을 알아야 할 필요가 있다.
SOP
Same Origin Policy, 동일 출처 정책
SOP는 말 그대로 동일 출처끼리만 자원을 공유하도록 하는 정책이라 보면 된다.
이를 바꿔말하면 다른 출처에서 가져온 자원과 상호작용하는 것을 제한하는 보안 방식이라 할 수 있다.
동일 출처끼리만 자원을 공유해야하는데, 다른 출처에서 해당 자원에 접근하기 위해 요청이 온다면 브라우저 입장에서 이는 '위협'이겠지? 따라서 보안상의 목적으로 잠재적으로 해로울 수 있는 출처(Origin)로부터의 요청을 제한함으로써 공격받을 수 있는 경로를 줄이기 위해 생겨난 개념이다.
따라서 우리가 다른 출처의 서버로부터 데이터를 전달받는 과정 상에서 마주하게 되는 CORS 에러는,
바로 이 브라우저 SOP 정책에 의해 요청이 막힌 상황을 말한다.
이때 여기서 말하는 출처란 아래와 같다.
❗️ 출처는 프로토콜, 호스트, 포트 조합으로 되어있으며, 이 중 하나라도 다를 시 동일 출처로 보지 않는다.
' 다른 출처의 요청을 허용해주는 게 CORS 인데,
왜 SOP가 아니고 CORS 이름의 에러가 발생하느냐? '
여기서 의문이 들 수 있는데,
브라우저 입장에서는 "다른 출처의 요청이 들어왔을 때
별도의 CORS 설정을 해주면 요청을 보내줄게" 라는 것으로 이를 이해하면 될 것 같다.
CORS 해결하기
CORS 에러는 프론트에서 서버에 접근하여 리소스를 가져오려 할 때,
출처가 같지 않다면 브라우저에 의해 CORS 에러가 발생한다는 것을 알게 됐다.
그렇다면, 이 CORS 에러는 어떻게 해결할 수 있을까?
결론부터 말하면 CORS 에러는 서버측에서 해결할 수도 있고, 클라이언트측에서 해결할 수도 있다.
(하지만 굳이 내 쪽에서 처리할 수 있는 일이라면 내 쪽에서 해결하는 게 상대측 업무 부담을 덜어주는 일이겠지?🙄)
프론트에서 처리한다 하더라도 상황에 따라 서버쪽에서 처리하도록 해야하는 경우라면, 서버 쪽에서 처리할 수 있는 방법도 안다면 협업에 있어서 많은 도움이 될거다.
프론트엔드에서 CORS 처리
# 프록시 서버 이용하기
앞선 포스팅에서 프록시 서버를 통해 우회하여 요청을 보냄으로써 CORS에러를 해결할 수 있었다.
프록시 서버는 클라이언트와 서버 사이에 중개 역할을 하는 서버를 말한다.
클라이언트가 서버에 요청을 보낼 때, 프록시 서버가 클라이언트 요청을 대신 받아 서버에 전달하고, 그 결과를 클라이언트에게 다시 전달하는 역할을 수행한다.
프록시 서버 설정 방법은 다음의 3가지 방법이 있다.
1. proxy 속성 사용 (package.json)
클라이언트 애플리케이션의 package.json 파일에 프록시 설정을 추가하는 방법이다.
이 방법은 주로 리액트 프로젝트에서 사용되며, package.json 파일에 "proxy" 프로퍼티를 추가하고 프록시 서버의 엔드포인트 주소를 설정한다. 그런 다음 요청을 보낼 때 프록시 서버의 주소를 생략하고 엔드포인트 주소만 작성하면 된다.
(Vue.js 프로젝트에서는 vue.config.js 파일에 "proxy" 프로퍼티를 추가하는 방법도 있는데,
이건 앞서 한 번 경험해봤으니까 이번엔 package.json 파일에 proxy 속성 추가하는 방법을 중점적으로 살펴보려 한다.)
(1) "package.json" 파일을 열고 다음과 같이 "proxy" 프로퍼티를 추가한다.
"proxy": "http://localhost:5000"
이렇게 설정하면 개발 서버는 모든 API 요청을 "http://localhost:5000"으로 프록시한다.
(2) API 요청을 할 때 엔드포인트 주소를 작성한다.
fetch('/api/data')
.then(response => response.json())
.then(data => {
// 데이터 처리 로직
})
.catch(error => {
// 에러 처리 로직
});
위 코드를 통해 "/api/data" 엔드포인트로 API 요청을 보내게 된다.
이렇게 하면 클라이언트 코드에서는 프록시 서버의 주소를 따로 지정하지 않고도, 개발 서버가 자동으로 "http://localhost:5000/api/data"로 요청을 전달한다.
✅ 엔드포인트(Endpoint)란?
엔드포인트는 직역하면 '끝 지점'이다.
한 마디로 URL의 끝 부분을 가리키며, 이는 웹 API에서 특정 리소스에 접근하기 위한 URL 경로를 말한다.
흔히 클라이언트가 특정 작업을 수행하거나 원하는 정보를 가져오기 위해 사용하는 경로다.
클라이언트는 이러한 엔드포인트를 사용하여 서버에서 데이터를 요청하거나 조작할 수 있다.
예를들어, 상황에 따라 다음과 같은 API 엔드포인트를 가질 수 있다.
1. 상품 목록 가져오기 엔드포인트:
- 엔드포인트 경로: /api/products
- 설명: 이 엔드포인트를 통해 클라이언트는 전체 상품 목록을 가져올 수 있다.
2. 특정 상품 정보 가져오기 엔드포인트:
- 엔드포인트 경로: /api/products/{상품ID}
- 설명: 이 엔드포인트를 통해 클라이언트는 특정 상품의 세부 정보를 가져올 수 있다. 상품ID는 동적으로 변경된다.
3. 장바구니에 상품 추가 엔드포인트:
- 엔드포인트 경로: /api/cart/add
- 설명: 이 엔드포인트를 통해 클라이언트는 장바구니에 상품을 추가할 수 있다.
2. Heroku의 우회 서버 사용
Heroku의 cors-anywhere 서비스를 사용하여 CORS 문제를 해결할 수 있다.
이 서비스를 통해 클라이언트 애플리케이션에서 요청을 보낼 때 프록시 서버를 사용하여 다른 도메인의 리소스에 접근할 수 있다. 요청할 때 URL에 http://cors-anywhere.herokuapp.com/를 추가하고 원래 엔드포인트 주소를 포함하면 된다.
http://cors-anywhere.herokuapp.com/http://{원래 엔드포인트 주소}
3. http-proxy-middleware 라이브러리 사용
이 라이브러리를 사용하면 클라이언트 애플리케이션에서 웹 소켓 통신 및 HTTP 요청의 프록시를 설정할 수 있다. 먼저 http-proxy-middleware 라이브러리를 설치하고, src/setupProxy.js 파일을 만들어서 프록시 설정을 추가하면 된다.
// src/setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function (app) {
app.use(
'/api',
createProxyMiddleware({
target: 'http://{프록시 서버 주소}',
changeOrigin: true,
})
);
};
백엔드에서 CORS 처리
# Access-Control-Allow-Origin 응답 헤더 추가
응답 헤더의 Access-Control-Allow-Origin 항목을 해당하는 Origin으로 설정해주면 CORS를 도입할 수 있다. Express의 경우는 다음과 같이 추가해 줄 수 있다.
app.use((req, res) => {
res.header("Access-Control-Allow-Origin", "*"); // 모든 도메인 허용
res.header("Access-Control-Allow-Origin", "http://localhost:3000"); // 특정 도메인 허용
});
다만 "*"를 이용해 모든 도메인을 허용해 놓는 것은 보안상 좋지 않아서 Origin을 특정해 주는 것이 좋다고 한다.
# CORS 미들웨어 사용하기
Express의 경우, cors 모듈을 사용하여 CORS를 도입할 수 있다.
import express from "express";
import cors from 'cors';
const app = express();
app.use(cors({ origin: [
'http://localhost:3000',
'https://devgenie.netlify.app'
]));
위와 같이 CORS를 설정한다면, localhost:3000과 nomalog.netlify.app라는 Origin에 대해 CORS를 허용할 수 있다.
요약
위 내용들을 간단하게 아래 그림과 같이 정리할 수 있을 것 같다.
'🖥 Computer Science > Network' 카테고리의 다른 글
[네트워크] REST API / Restful API란?(feat. 원칙과 네이밍 규칙) (0) | 2023.09.10 |
---|---|
[네트워크] 인터넷과 HTTP/S, DNS, 웹소켓, SSL(TLS) 개념정리 (0) | 2023.08.22 |
dev_genie
@dev_genie
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!