Post

CORS(교차 출처 리소스 공유), SOP(동일 출처 정책)

CORS란?

Cross-Origin Resource Sharing, 즉 교차 출처 리소스 공유이다. (잘 안 외워진다..)

결론부터 말하자면, CORS는 HTTP 헤더(Access-Control-Allow-Origin)를 사용하여 한 출처에서 다른 출처의 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다.

CORS 정책을 위반하면 위와 같은 CORS policy 오류 메세지가 뜬다.

여기서 출처란 무엇이고, 교차 출처는 또 무엇일까?

출처

출처(Origin)란 URL구조의 protocol, host, port를 합친 것을 말한다.

콘솔 창에서 location.origin을 치면 출처를 확인할 수 있다.

동일 출처라는 조건을 만족하려면 protocol, host, port가 동일해야 한다.

동일 출처 정책

Same-Origin Policy(SOP)는 같은 출처에서만 리소스를 공유할 수 있다는 규칙을 가진 보안 정책이다.

당연한 말이지만 동일 출처 정책을 사용하면 보안성이 높아진다.

하지만 웹 개발을 하다보면 외부에서 데이터를 요청해야 할 때가 있는데, 출처가 다르더라도 CORS 정책을 지킨 리소스 요청은 SOP 정책에서 예외로 허용해준다.

다른 출처의 리소스를 불러오려면 그 출처에서 올바른 CORS 헤더를 포함한 응답을 반환해야 하는 것이다.

CORS의 동작원리

  1. 기본적으로 웹에서 다른 출처의 리소스를 요청할 때 HTTP 프로토콜을 사용하는데, 이때 브라우저는 요청 헤더의 Origin 필드에 출처를 담아 보낸다.
  2. 이후 서버는 응답 헤더의 Access-Control-Allow-Origin 값에 이 리소스를 접근하는 것이 허용된 출처를 내려준다.
  3. 응답을 받은 브라우저는 자신이 보냈던 요청의 Origin과 서버가 보내준 응답의 Access-Control-Allow-Origin을 비교하여 유효한 응답인지를 결정한다.

여기서 중요한 것은 출처를 비교하는 로직이 브라우저에 구현되어 있는 스펙이라는 것이다.

CORS 정책을 위반하더라도 서버는 정상적으로 응답(status 200)을 보내고, 브라우저 측에서 출처를 체크한다.

CORS의 동작방식

Preflight Request

  • 가장 일반적인 방식으로, 브라우저에서 요청을 한번에 보내지 않고 예비 요청(Preflight)과 본 요청으로 나누어서 서버로 전송한다.

  • 예비 요청에는 HTTP 메소드 중 OPTIONS 메소드가 사용된다.

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

  • 이후 브라우저는 자신이 보낸 예비 요청과 서버가 응답한 허용 정책을 비교한 후, 이 요청을 보내는 것이 안전하다고 판단되면 같은 엔드포인트로 다시 본 요청을 보낸다.

Simple Request

  • 단순 요청은 예비 요청을 보내지 않고 본 요청을 바로 보낸다.

  • 아래 조건을 만족하는 경우에만 예비 요청을 생략할 수 있다.

    • HTTP 메서드 중 GET, HEAD, POST를 사용하는 경우
    • 자동으로 설정되는 헤더 외에 수동으로 설정할 수 있는 헤더는 Accept, Accept-Language, Content-Language, Content-Type 뿐이다.
    • Content-Type 헤더에는 application/x-www-form-urlencoded, multipart/form-data, text/plain 값만 허용된다.
  • 보통 웹에서 요청을 보낼 때 Content-Type 헤더에 text/xml이나 application/json을 설정해야 하기 때문에 사실상 조건을 충족시키기 어렵다.

CORS 해결방법

Access-Control-Allow-Origin 세팅

  • 서버에서 Access-Control-Allow-Origin 헤더에 알맞은 값을 세팅해준다.

  • 와일드카드인 *으로 세팅하면 보안성이 취약해지므로 출처를 명시해주어야 한다.

  • 서버에서 미들웨어 등을 사용하여 접근 가능하도록 세팅할 수 있다.

Webpack Dev Server로 리버스 프록싱

로컬에서 개발을 하는 경우 webpack-dev-server가 제공하는 프록시 기능을 사용하면 CORS 정책을 우회할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
  devServer: {
    proxy: {
      "/api": {
        target: "https://api.evan.com",
        changeOrigin: true,
        pathRewrite: { "^/api": "" },
      },
    },
  },
};

위와 같이 devServer.proxy를 설정하면 /api로 요청을 보냈을 때 웹팩에서 localhost:8000/apihttps://api.evan.com로 프록싱해주기 때문에 마치 CORS 정책을 지킨 것처럼 서버와 자유롭게 통신을 할 수 있다.

express로 서버를 실행하는 경우 http-proxy-middleware 라이브러리를 사용하면 된다.

추가

SOP는 HTML 문서 안에 포함된 image, css, script에는 적용되지 않는다.

하지만 CORS를 통과해도 응답에 담긴 내용에는 접근할 수 없다.

1
2
<img src="https://evanmoon.tistory.com/rss" />
<script src="https://evanmoon.tistory.com/rss"></script>

참고사이트

CORS는 왜 이렇게 우리를 힘들게 하는걸까?
교차 출처 리소스 공유 (CORS)

This post is licensed under CC BY 4.0 by the author.