Post

CORS

웹 개발을 하게 되면 한번씩은 다 겪는 에러이자 골머리를 썩게 만드는 부분이다.
이 CORS라는게 당최 뭔지 문제가 발생 했을 때 어떻게 해결하면 되는지에 대해서 오늘은 알아볼 생각이다.

CORS란?

먼저 CORS는 Cross-origin-resource-sharing의 약자이다. 직역하자면 엇갈린 출처 리소스 공유 (정책)인데 여기서 엇갈린 출처라는게 중요하다.

여기서 말하는 출처란 이런 것이다.
우리가 어떤 사이트에 접속할 때 우리는 URL을 통해서 접속하게 되는데 가령 https://naver.com/blog?id=test 라는 URL을 통해서 접속한다고 한다면 여기서 출처는 프로토콜인 https와 naver.com인 사이트 도메인, 그리고 대부분 443이나 80이라서 자주 생략되는 포트번호를 뜻한다.

우리가 특정 사이트에 접속하였다는것은 그 사이트가 속한 출처로부터 페이지나 스크립트, 즉 리소스를 받아오는 것이다. 그런데 이 리소스가 동일 출처에서 온게 아니라면 공유 할 수가 없다.

가령 https://naver.com에 접속했는데 https://daum.net에서 가져온 리소스를 사용할 수 없다는 말이다. 이건 서버가 아니라 페이지를 열람하는 브라우저에서 막는 것이다.

하지만 인터넷을 쓰다보면 다른 출처에 있는 리소스를 사용하는 경우가 있는데 이런 경우까지 다 막아버리면 아무것도 할수가 없다. 그래서 웹에서 리소스를 갖고오고 사용하는 것은 기본적으로 출처 정책을 따른다.

출처 정책의 차이

이 출처 정책은 태그 별 함수별로 다른데, 세부 내용은 아래와 같다.

  1. 교차 출처 지원하는 태그의 경우 - CORS 정책에 따른 Block 발생하지 않음
    • img나 video, script 태그의 src, link는 기본적으로 교차 출처 사용이 가능하기 때문에 브라우저에서 차단이 발생하지 않는다.
  2. XMLHttpRequest, Fetch API 스크립트 - CORS 정책에 따른 Block 발생함
    • 다른 도메인 소스에 대한 ajax 청이나 다른 도메인 폰트 사용시 차단 된다
    • 기본적으로 동일 출처를 사용해야한다.

2번과 같은 경우에서는 전체를 다 막아버리는데 그럼 어떻게 해야하는가? 이럴때 필요한게 CORS에 대한 정책이다. 다른 출처에서 갖고 왔을 지라도 CORS 정책을 준수한다면 허용이 가능하다.

CORS 정채의 작동 방식

CORS 정책은 일반적으로 3가지로 작동한다.

  1. 예비요청에 의한 CORS
    • 실제 요청전 OPTION 요청에 의해 해당 서버로 요청을 보내는데, Origin 헤더에 출처를 넣고, Access-Control-Request-Method 헤더에 실제 요청에 사용할 메소드를 설정하고 Access-Control-Request-Headers 헤더에 실제 요청에 사용할 헤더들을 설정한다.
    • Access-Control-Allow-Origin 헤더에 허용되는 Origin들의 목록을 설정한다. Access-Control-Allow-Methods 헤더에 허용되는 메소드들의 목록을 설정한다. Access-Control-Allow-Headers 헤더에 허용되는 헤더들의 목록을설정한다. Access-Control-Max-Age 헤더에 해당 예비 요청이 브라우저에 캐시 될 수 있는 시간을 초 단위로 설정한다.
    • 브라우저에서 보낸요청과 응답 정책을 비교하여, 확인 후 요청을 보낸다.
    • 서버가 응답하면 데이터를 브라우저에서 처리한다.
  2. 단순 요청
    • 이 경우 예비 요청을 생략한다. 그렇기 때문에 본 요청에 대한 응답에 서버가 Access-Control-Allow-Origin 헤더를 달아서 보낸다.
    • GET, HEAD, POST 요청 중 하나여야한다.
    • Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width 헤더일때만 해당된다.
    • Content-Type 헤더가 application/x-www-form-urlencoded, multipart/form-data, text/plain일때만 해당된다.
  3. 인증된 요청
    • 자격 인증 정보를 실어 요청할때를 말한다.
    • 클라이언트에서 credentials 옵션에 3가지 값을 사용하여 지정가능하다 : same-origin, include, omit
    • 이러한 요청에서 응답시 서버에서 헤더는 아래와 같이 사용할수있다.
      응답 헤더의Access-Control-Allow-Credentials 항목을 true로 설정해야 한다.
      응답 헤더의Access-Control-Allow-Origin 의 값에 * 문자는 사용할 수 없다.
      응답 헤더의Access-Control-Allow-Methods 의 값에 * 문자는 사용할 수 없다.
      응답 헤더의Access-Control-Allow-Headers 의 값에 * 문자는 사용할 수 없다. 위와 같은 사항을 준수하면 CORS 정책을 통과하게 된다.

대부분이 이 3가지 시나리오에 의해 작동된다.

문제가 생겼을때 해결법

사실은 대부분 2번이 문제지만 개발간에도 문제가 생기는 경우가 많으므로 두 가지에 대해서 다 이야기해보겠다.

개발 환경에서 문제가 생겼을 경우

  1. 크롬 확장 프로그램을 사용한다.
    • CORS 정책 위반에 의한 차단은 브라우저에서 일어나는 것이므로 애당초 브라우저에서 차단을 풀어주는 것이다.
  2. 프록시 사이트를 이용한다.
    • 모든 출처를 허용한 서버를 이용하여 요청을 하는 방식과 동일하다. 별도로 구축하거나, 시중의 무료 서비스를 사용할 수 있다.

실제 배포 상황에서 문제가 생겼을 경우

  1. 서버에서 Access-Control-Allow-Origin 헤더 세팅 한다.
    • node, nginx, tomcat등 대상이 모두 달라도 헤더 설정은 지원한다.
    • 필요한 서버 URL을 포함하여 세팅한다.
    • 귀찮다고 *로 설정해버리면 보안 문제가 생긴다 : 해커의 가짜 사이트에서 스크립트가 넘어와서 실행될 수도 있다

번외 : 제대로 된 CORS 정책 정립 하지 않는 경우 및 파생되는 문제점

제대로 된 CORS 정책이 아닌 경우

  1. Access-Control-Allow-Origin *로 설정하는 경우
  2. 요청 받은 Origin을 Access-Control-Allow-Origin로 그대로 쓰는 경우
    • 사실상 Access-Control-Allow-Origin를 *로 설정하는 것과 다를 바 없다.
  3. Origin이 null인 경우
    • 개발할때 null로 해두고 별 생각없이 Release 해버려 문제가 된다.

파생되는 문제점

아래의 공격에 취약해진다.

  1. CSRF(Cross Site Request Forgery)
    • 사이트 간 요청 위조에 대한 것으로 외부 사이트에 대한 요청을 넣어서 해당 사용자에게 작동시킬 수 있다.
  2. XSS(Cross Site Scripting)
    • 특정 스크립트를 넣어두고 외부에서 스크립트를 가져와 실행하게 할 수 있다.
    • Stored XSS의 방식으로 하는 경우가 대부분이다.
  3. Sensitive Data Exposure
    • 사실 이건 위에 두가지 방법으로 일어날 수 있는 문제이다. 민감한 데이터가 노출되는걸 통틀어서 말하는 것이다.
This post is licensed under CC BY 4.0 by the author.