CS/Web

CORS 에러란? (w. 해결방법)

NaDuck 2023. 6. 21. 11:51

CORS(Cross-Origin Resource Sharing)

  • 다른 출처의 리소스 공유에 대한 허용/비허용 정책
  • 한 출처에 있는 리소스에서 다른 출처에 있는 리소스에 안전하게 접근하도록 하는 메커니즘

 

 

출처란?

출처) https://beomy.github.io/tech/browser/cors/

  • URL의 구성 요소 중 프로토콜 + host + port 3가지가 같으면 origin(동일 출처)라고 하며, 이 중 하나라도 다르다면 other origin(다른 출처)이라고 한다. 

ex)

1. http://example.com/app1
2. https://example.com/app2
프로토콜이 다름
1. http://example.com
2. http://www.example.com
3. http://myapp.example.com
host가 다름
1. http://example.com
2. http://example.com:8080
80, 8080 포트가 다름 (기본 http 포트: 80)

 

 

요청 방식에 따라 다른 CORS 발생 여부

1. <img>, <script>, <video>, <link> 태그 등

<link rel="stylesheet" href="…" />
<script src="…"></script>
<img src="…" />
  • 기본적으로 Cross-Origin 정책을 따른다.
  • <link> 태그의 href 속성에서 다른 사이트의 .css 리소스에 접근하는 것이 가능
  • <img> 태그의 src에서 다른 사이트의 .png, .jpg 등의 리소스에 접근하는 것이 가능
  • <script> 태그의 src에서 다른 사이트의 .js 리소스에 접근하는 것이 가능 (type=”module”은 제외)

 

2. XMLHttpRequest, Fetch API 스크립트

  • 기본적으로 Same-Origin 정책을 따른다.
  • 자바스크립트에서의 요청은 기본적으로 서로 다른 도메인에 대한 요청을 보안상 제한한다. 브라우저는는 기본으로 하나의 서버 연결만 허용되도록 설정되어 있기 때문이다.

 

 

SOP(Same-Origin Policy)가 왜 필요할까?

다른 출처 요청의 위험성

  • SOP 정책이 없어 서로 다른 출처의 두 어플리케이션이 자유롭게 소통할 수 있는 환경이 된다면, 악성 사이트에서 원래 사이트를 흉내내어 사용자가 로그인을 하도록 유도하고, 로그인했던 세션을 악의적으로 이용해 사용자 정보를 탈취하는 등의 공격을 할 수 있다.
  • 따라서 SOP 정책으로 동일하지 않은 출처의 스크립트가 실행되지 않도록 브라우저에서 사전에 방지한다. 즉, 다른 출처로부터의 공격을 예방하기 위한 보안상의 이유!

 

 

CORS 에러란?

  • 위와 같이 SOP 정책에 따라 다른 출처의 리소스를 차단하면서 발생한 에러를 CORS 에러라고 한다.

ex) 같은 외부 image를 <img> 태그와 ajax 요청으로 가져오기

        fetch('<<a href=https://third-party-test.glitch.me/check.svg>https://third-party-test.glitch.me/check.svg</a>>')
            .then(response => response.blob())
            .then(imgBlob => {
                const imageObjectURL = URL.createObjectURL(imgBlob); // 응답 받은 이미지를 blob 객체로 변환
                const img = document.createElement('img'); // 이미지 태그를 생성하고
                img.src = imageObjectURL; // 이미지 경로를 설정한뒤
                document.body.append(img); // html에 추가
            })
    

⇒ <img> 태그로 이미지를 잘 가져오지만, ajax 요청에선 CORS error가 발생

 

💡 참고로 이러한 출처 비교 & 차단은 '브라우저'에서 이뤄진다. 즉 서버 단에서의 다른 출처 요청은 CORS 에러에 자유로운데, 그래서 이를 이용한 Proxy(프록시) 서버라는 것도 있다.

 

 

브라우저의 CORS 동작 과정

다음은 기본적인 동작 흐름이며 이외에 여러 동작 방식들이 있다. (아래 참고)

  1. 클라이언트 → 서버로 http 요청을 보낼 때, 헤더에 Origin을 담아 전달한다. 브라우저는 요청 헤더에 Origin이라는 필드에 자신의 출처를 함께 담아 보낸다.
  2. 서버는 응답 헤더에 Access-Control-Allow-Origin을 담아 클라이언트로 전달한다. 이 때 담기는 값은 ‘이 리소스를 접근하는 것이 허용된 출처 url’이다.
  3. 브라우저는 자신이 보냈던 요청의 Origin과 서버가 보내준 응답의 Access-Control-Allow-Origin을 비교한다.
  • 만약 유효하지 않다면 그 응답을 사용하지 않고 버림 → CORS 에러 발생
  • 유효할 경우 다른 출처의 리소스를 문제없이 가져온다.

 

💡 CORS가 동작하는 방식들

1. Simple Request(단순 요청)

  • 예비 요청(preflight)을 생략하고 바로 서버에 본 request를 보낸 뒤, 서버가 이에 대한 response 헤더에 Access-Control-Allow-Origin 헤더를 보내주면 브라우저가 CORS 정책 위반 여부를 검사하는 방식
  • 단순 요청인 만큼, 요청이 가능한 조건이 까다로운데, 아래의 조건들이 있다.

    (1) 요청의 메소드는 GET, HEAD, POST 중 하나여야 한다.

    (2) Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width 헤더일 경우에만 적용된다.

    (3) Content-Type 헤더가 application/x-www-form-urlencoded, multipart/form-data, text/plain 중 하나여야한다. 아닐 경우 예비 요청(preflight)으로 동작된다.
  • 위의 조건을 모두 만족해야 simple request가 일어나기 때문에 이 상황은 드물게 일어난다고 한다. 왜냐하면 대부분의 Http API 요청은 text/xml 또는 application/json으로 통신하기 때문이다.

 

2. Preflight Reqeust(예비 요청)

  • OPTIONS 메소드로 http 요청을 미리 보내 실제 요청을 전송하기에 안전한지 확인한다.

 

3. Credentialed Request(신용 요청)

  • 쿠키, 인증 헤더, TLS 클라이언트 인증서 등의 신용 정보와 함께 요청한다.

 

 

CORS 에러 해결 방법

⇒ 결론적으로, CORS 해결책은 서버의 허용이 필요하다.

  • 서버에서 Access-Control-Allow-Origin값에 허용할 출처를 기재해서 클라이언트에 응답하면 된다. 즉 백엔드 쪽에서 고쳐야할 부분 (참고로 클라이언트에서 자바스크립트로 origin 헤더값을 조작해도, 브라우저에서 이를 감지하여 차단하므로 불가능하다.)

 

1. Chrome 확장 프로그램 이용하기 Allow-CORS

  • 로컬 환경에서 API 테스트할 때 CORS 에러를 해결할 수 있다.

 

2. Proxy(프록시) 사이트 이용하기

  • 클라이언트에서 직접 서버에 리소스를 요청했을 때, 서버에서 따로 설정을 안해줘서 CORS 에러가 뜬다면, 모든 출처를 허용하는 서버 대리점을 통해 요청을 할 수 있다.
  • 즉 프록시는 클라이언트-서버 사이의 중계 대리점이라고 보면 된다.
  • 그러나 이 또한 악용 사례가 있으므로 api 요청 횟수 제한을 두기 때문에 테스트용 등으로 간단하게 사용할 수 있다. 실제 사용 시, 직접 프록시 서버를 구축해서 사용해야 한다.

 

3. 서버에서 Access-Control-Allow-Origin 헤더를 세팅하기(정석적인 해결책)

  • 직접 서버에서 Http 헤더 설정을 통해 출처를 허용하게 설정한다.
  • 서버는 Node.js, Spring 등 여러가지가 있어, 각 서버의 문법에 맞게 설정한다.

ex) Node.js 서버

var http = require('http');

const PORT = process.env.PORT || 3000;

var httpServer = http.createServer(function (request, response) {
    // Setting up Headers 👈
    response.setHeader('Access-Control-Allow-origin', '*'); // 모든 출처(orogin)을 허용
    response.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); // 모든 HTTP 메서드 허용
    response.setHeader('Access-Control-Allow-Credentials', 'true'); // 클라이언트와 서버 간에 쿠키 주고받기 허용

    // ...

    response.writeHead(200, { 'Content-Type': 'text/plain' });
    response.end('ok');
});

httpServer.listen(PORT, () => {
    console.log('Server is running at port 3000...');
});
  • 위의 코드에선 모든 출처를 허용하게 설정했지만, 모든 Origin에서 오는 요청을 허용하기 때문에 그만큼 위험성이 증가하므로, 웬만해서 출처를 직접 명시해주기

 

 

Reference

 

🌐 악명 높은 CORS 개념 & 해결법 - 정리 끝판왕 👏

악명 높은 CORS 에러 메세지 웹 개발을 하다보면 반드시 마주치는 멍멍 같은 에러가 바로 CORS 이다. 웹 개발의 신입 신고식이라고 할 정도로, CORS는 누구나 한 번 정도는 겪게 된다고 해도 과언이

inpa.tistory.com

 

CORS란 무엇인가?

개요 웹 프로그래밍에서 프런트와 백엔드 작업을 하면, 한번씩 발생하는 문제가 CORS 문제입니다. 현재 하는 업무가 이런 경우는 없었지만, 개인 프로젝트 시 발생했던 문제를 기억하며 해당 문

escapefromcoding.tistory.com

 

CORS란 무엇인가? – Yunseok's Dev Blog

 

hannut91.github.io