티스토리 뷰

개발/CS, Network

SOP와 CORS

hahagarden 2023. 4. 4. 16:29

SOP(Same Origin Policy)

동일 출처 정책. 같은 출처의 리소스만 공유가 가능하다는 정책이다. 

http://naver.comhttps://naver.com 과 프로토콜이 다르기 때문에 출처가 다르다.
https://hahagarden.github.iohttps://github.io 와 호스트가 다르기 때문에 출처가 다르다.
http://hello.world:81http://hello.world 와 포트번호가 다르기 때문에 출처가 다르다.(http 프로토콜의 기본 포트번호는 :80이다)  

SOP에 따라 다른 출처의 리소스는 기본적으로 공유를 제한하기 때문에 정보가 다른 곳으로 새어나가는 것을 방지할 수 있다. 방지책이 없다면 사용자가 깜빡하고 로그아웃을 안하거나 공유 기기를 사용할 때 해킹 등의 위험이 클 것이다.
SOP는 이러한 보안상의 이점 때문에 모든 브라우저에서 기본적으로 사용하고 있는 정책이다.

 

CORS(Cross Origin Resource Sharing)

출처가 다른 리소스들을 사용할 일은 너무나도 많다. 개발 중인 웹사이트에서 네이버 지도 api를 사용하고 싶다면? github정보를 받아와서 사용하고 싶다면? 모두 출처가 다르다. 어떻게 사용할 수 있을까? 

 

SOP와 CORS
CORS 에러

CORS는 교차 출처 리소스 공유 라고 해석할 수 있다. 예전에 개인 프로젝트를 할 때 CORS에러를 마주친 적이 있다. 당시에는 어떻게 해결은 했지만 '도대체 CORS가 뭐야, 이게 왜 있는거야! 😠 ' 라는 마음이었다. 하지만 CORS는 우리를 도와주는 정책이었다. SOP정책으로 인해 같은 출처가 아닌 자원은 이용을 못하는데, CORS정책을 통해 추가 HTTP 헤더를 사용하여 어떤 것을 설정해줌으로써 다른 출처의 리소스를 사용할 수 있는 권한을 얻는다. 

참고로, CORS에러가 현시가 될 때에도 서버는 정상적으로 응답을 한 것이다. 안전하다는 정보가 불충분할 뿐, 서버는 최대한 대답을 했다. 브라우저 단에서 응답을 분석하여 출처를 비교하고, 다른 출처이면서 안전하다는 정보가 불충분하면 브라우저가 CORS 에러를 띄우는 것이다. CORS에러는 브라우저의 몫이다. 그러므로 브라우저를 이용하지 않는 경우에는 CORS에러가 뜨지 않는다. 브라우저를 통하지 않고 서버끼리 통신을 할 때에는 CORS에러가 뜨지 않는다. 이를 이용한 것이 프록시 서버라고 한다.

 

CORS 설정 방법

서버의 응답 헤더에 Access-Control-Allow-Origin을 작성하는 등 CORS설정을 해주면 접근 권한을 얻을 수 있다.
CORS설정 방법은 서버 환경에 따라 다르다. 

// Node.js로 간단한 HTTP서버를 만드는 경우
const http = require('http');

const server = http.createServer((request, response) => {
// 모든 도메인
  response.setHeader("Access-Control-Allow-Origin", "*");

// 특정 도메인
  response.setHeader("Access-Control-Allow-Origin", "https://codestates.com");

// 인증 정보를 포함한 요청을 받을 경우
  response.setHeader("Access-Control-Allow-Credentials", "true");
})
// Express 프레임워크로 서버를 만드는 경우 cors() 미들웨어 사용
const cors = require("cors");
const app = express();

//모든 도메인
app.use(cors());

//특정 도메인
const options = {
  origin: "https://codestates.com", // 접근 권한을 부여하는 도메인
  credentials: true, // 응답 헤더에 Access-Control-Allow-Credentials 추가
  optionsSuccessStatus: 200, // 응답 상태 200으로 설정
};

app.use(cors(options));

//특정 요청
app.get("/example/:id", cors(), function (req, res, next) {
  res.json({ msg: "example" });
});

 

CORS 동작 방식

1. 단순 요청(Simple Request)

요청이 특정 조건을 만족하는 경우 출처가 달라도 요청을 보낼 수 있다. 단순 요청 방식이다. 다음의 세가지 조건을 만족하면 단순 요청이 바로 전송되고 그렇지 않은 경우 다음 설명할 프리플라이트 요청이 전송된다.

  • Method: GET, POST, HEAD 중 하나여야 한다.
  • Content-Type: application/x-www-form-urlencoded, multipart/form-data, text/plain 중 하나여야 한다.
  • Headers: Accept, Accept-language, Content-Language, Content-Type 중 하나여야 한다.

그런데 대부분의 어플리케이션의 application/json으로 통신하여 Content-type이 위반된다. 이렇듯 단순요청의 조건을 충족하는 경우가 많지 않기 때문에 대부분의 API요청은 다음 설명할 프리플라이트 요청으로 이루어진다고 보면 된다. 

 

2. 프리플라이트 요청(Preflight Request)

출처 mdn

CORS에서 단순요청의 조건을 충족하지 못하면 실제 요청을 하기 전에 사전에 점검을 받는 프리플라이트 요청이 전송된다. 브라우저가 서버에 요청을 하기 전에 preflight라는 HTTP요청을 먼저 전송한다. 프리플라이트 요청에 대한 응답을 받고 응답 헤더의 Access-Control-Allow-Origin으로 안전하다는 판단이 서면 브라우저가 실제 요청을 서버로 보낸다. 프리플라이트 요청을 했을 때 안전하지 않다면 CORS에러가 나타난다.

 

3. 인증정보를 포함한 요청(Credentialed Request)

클라이언트가 서버에 요청을 할 때 자격인증정보(Credential)를 함께 보낸다. Credential은 세션ID가 들어있는 쿠키나 Authorization 헤더에 설정하는 토큰 값이다. 대부분의 요청에서 일반적으로 전송하는 JSON이 아닌 이러한 자격인증정보 데이터가 함께 전송되면 인증정보를 포함한 요청으로 전송된다.

클라이언트가 서버에게 Credential을 전송하는 방법은 fetch 메서드, axios, jQuery 라이브러리 등 모두 문법이 다르다. 또한 다른 방법들과 다르게 서버가 Credential에 대한 처리도 해주어야 한다. Access-Control-Allow-Origin 에 모든 출처를 허용한다는 뜻의 와일드카드(*)를 사용할 수 없고 특정 출처를 명확하게 설정해야 한다.

 

Reference

https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F

코드스테이츠 학습자료

반응형
댓글