본문 바로가기
웹 개발/error

[error/security] CORS란? Origin이란?

by dani0312 2024. 1. 10.

CORS는 개발을 하다보면 매우 자주 맞닥뜨리게 되는 이슈이다. 프론트와 백엔드를 함께 작업을 하게 되면 마주치곤 한다. 이는 사용자의 잘못이 아닌 사용자를 보호하기 위해 걸려있는 웹브라우저의 보안 정책이다. 

서버 측의 문제가 아니라 브라우저의 측의 문제임을 기억해야 한다.

 

 

📌CORS와 Origin의 의미

◾CORS란?

Cross-Origin Resource Sharing의 약자이다. 출처가 다른 자원들을 공유한다는 뜻으로, 한 출처에 있는 자원에서 다른 출처에 있는 자원에 접근하도록 하는 개념이다. 직역하면 교차되는 출처 자원들의 공유이다. 예를 들면 출처A에서 출처B로 자원을 요청한다고 하면, 이를 교차 출처 요청이라 부른다. 

 

브라우저에서는 보안의 이유로 이렇게 교차 출처의 HTTP 요청을 제한한다. 그래서 다른 출처로 요청을 하게 되면 이 CORS를 해결해야 원하는 정보를 불러올 수 있고 그렇지 않으면 CORS이슈가 발생되며 우리가 원하는대로 서비스가 되지 않을 것이다. 브라우저에서는 이 정책을 통해 교차 출처의 자원을 안전하게 사용할 수 있도록 하는 것이다. (이에 대한 것은 동일 출처 정책과 관련이 있는데 이는 아래에 설명이 더 자세히 나와있다.)

 

그렇다면 정확히 다른 출처를 말하는 교차 출처(Cross-Origin)란 무엇일까? 

 

 

◾Origin (출처) 이란?

Cross-Origin을 직역하면, 교차 출처라고 할 수 있다. A라는 출처에서 B라는 출처로 요청을 보내면 다른 출처이기에 교차 출처라고 할 수 있다. 그렇다면 출처(Origin)의 기준은 무엇일까? 

 

아래는 한 블로그에서 잘 정리된 이미지를 가져왔다. URL의 구성요소가 잘 표현되어 있다. 이를 통해 `출처`의 개념을 알아보자.

 

https://beomy.github.io/tech/br owser/cors/

 

위의 구성요소 중 Protocol + Host + Port 3가지가 같으면 동일 출처(Origin)라 한다. 이 3가지 중 하나라도 다르면 다른 출처이다. 

 

💡 Origin : Protocol + Host + Port

 

프로토콜의 HTTP는 80번, HTTPS는 443번 포트를 사용하는데, 80번과 443번 포트는 생략이 가능하다. 이에 대한 설명은 아래를 참조하자

 

💡80번, 443번 포트는 생략가능, 다른 포트는 NO!
HTTP의 기본 포트는 80번, HTTPS의 기본 포트는 443번이다.
따라서 대부분의 웹 브라우저 및 웹 서버는 url에서 이런 포트 번호를 생략할 수 있다.
만약 생략 된 경우 브라우저는 기본 포트를 사용한다. 
EX) `http://example.com:80`을 생략하여 `http://example.com`와 같이 쓸 수 있는 것이다. 

 

 

◾같은 출처 vs 다른 출처

위의 단락에서 같은 Origin(출처)이란 Protocol + Host + Port 이 3가지가 모두 동일해야 한다고 하였다.

이를 기억하며 아래 동일한 출처가 되는 것과 아닌 케이스들을 보자

 

● 동일한 출처

http://example.com
http://example.com:80
http의 기본 포트 번호 80이 생략되어 있으므로 같은 출처이다. 
http://example.com/app1/index.html
http://example.com/app2/index.html
 Protocol, Host, Port(생략)이 같으며, Path부터 다르므로 같은 출처이다.
https://example.com/app1/index.html
https://example.com/app1/about?q=work
Protocol, Host, Port(생략)이 같으므로 같은 출처이다. 

 

 

●  다른 출처

http://example.com
https://example.com
Protocol 다름
http://example.com
http://www.example.com
Host 다름
http://example.com
http://example.com:81
 Port다름

 

 

 

📌동일 출처 정책(Same-Origin Policy)

◾동일 출처 정책이란?

출처(Origin)에 대해 알아보았으니 이제 동일 출처 정책을 이해할 수 있다.

 

혼자 개발을 하여 Postman으로 API를 테스트하거나, 다른 서버에서 API를 호출할 때는 잘 동작을 하다 브라우저에서 API를 호출을 할 때 CORS policy 오류가 발생하여 당황스러운 경험이 있을 수 있다. 그 이유는 브라우저가 동일 출처 정책(Same Origin Policy, SOP)을 지켜 다른 출처의 리소스 접근을 금지하기 때문이다.  동일 출처 정책은 웹 브라우저에서 자바스크립트로 다른 출처의 리소스에 접근하는 것을 제한하는 보안 정책이다. 즉, 위에서 알아본대로 동일출처가 아닌 다른출처에 해당하는 경우, 다른 출처의 자원을 함부로 가져오지 못하게 하는 것이다.

 

◾동일 출처 정책을 보안하는 CORS정책

이는 보안을 강화하기 위한 것이지만, 현대 웹 애플리케이션에서는 다른 출처의 서버에서 데이터를 가져와야 하는 경우가 많다. 그렇기에 몇 가지 예외 조항을 두고 다른 출처의 리소스 요청이라도 이 조항에 해당할 경우에는 허용하기로 하였는데, 그 중 하나가 바로 CORS정책을 지킨 리소스 요청이다.

이런 경우 CORS가 사용되어, 서버측에서 특정 출처에 대한 요청을 허용하도록 브라우저에게 알려준다.

 

CORS는 HTTP헤더를 사용하여 브라우저가 다른 출처의 자원에 대한 요청을 허용할지 여부를 결정한다. 서버에서는 특정 출처에 대한 요청을 허용하기 위해 'Access-Control-Allow-Origin'헤더를 설정하고, 필요에 따라 다른 CORS관련 헤더들을 설정한다. 

 

즉, SOP(Same Origin Policy) 정책이 있더라도 CORS정책에 따르면 다른 출처의 리소스도 허용한다는 의미이다. 

 

 

📌CORS 해결 방법

◾CORS 해결 방법

CORS 이슈가 발생한다면, 다른 origin으로 요청을 보낸 경우일 것이다. CORS정책에 따라서 이 응답을 받으려면 어떻게 해야할까? 아래는 대표적인 방법들이다.


1. API 서버 측에 Access-Control-Allow-Origin 헤더 추가
2. 클라이언트에서 프록시 사용 
3. 서버측에서 프록시 사용

 

1번은 정석적인 방법이며, 서버 측에 헤더를 추가해야 하니 자원을 가져오고자 하는 서버를 다룰 수 있는 환경에서 사용할 수 있는 방법이다. 2번과 3번은 CORS문제를 우회하는 일반적인 해결 방법들이다.

 

1.  서버 측에서 CORS 헤더 설정

직접 서버에서 HTTP 헤더 설정을 통해 출처를 허용하게 설정하는 방법이다. 가장 정석적인 해결책이다. 서버의 종류도 노드 서버, 스프링 서버, 아파치 서버 등 여러가지가 있어 내가 사용하는 서버에 맞는 문법에 맞게 HTTP 헤더를 추가해주면 된다. 

 

2. 클라이언트에서 프록시 사용

클라이언트에서 자체적으로 서버에 직접 요청하지 않고, 프록시 서버에게 요청한다. 프록시 서버는 실제 서버에 대한 요청을 수행하고, 그 결과를 클라이언트에게 반환한다. 이렇게 하면 클라이언트는 프록시 서버와 동일 출처이므로 브라우저에서 CORS에러가 발생하지 않는다.

 

프론트엔드 프레임워크를 사용하는 경우, 프론트엔드 내부적으로 프록시를 호출하는 미들웨어를 사용할 수 있다.

 

 

3. 서버 측에서 프록시 사용

클라이언트 -> [ 프록시 서버 -> 서버 ]

         요청                요청

 

클라이언트 <- [ 프록시 서버 <- 서버 ]

         응답                 응답

 

Nginx같은 서버인 프록시, 즉 리버스 프록시를 활용하는 방법이다. 이 방법은 실제 서로 다른 프론트엔드와 백엔드 서버를 마치 하나의 서버인 것처럼 만들 수 있다. 웹 브라우저 입장에서는 클라이언트와 서버, 이 둘이 동일한 Origin을 가지고 있기 때문에 CORS이슈가 발생하지 않을 것이다.

 

프록시 서버는 클라이언트로부터 받은 요청을 가로채고, 이를 클라이언트가 원하는 자원에 대한 요청을 서버에 대신 전달한다. 클라이언트는 프록시 서버에게 요청을 보내면 프록시 서버가 클라이언트의 요청을 받아서 필요한 처리를 수행하고, 그 후에 실제 서버에게 해당 요청을 전달하게 된다. 

 

이 방법은 백엔드 서버를 Nginx 뒤쪽에 감춰서 클라이언트가 백엔드 서버의 존재를 모르게 하는 것이다. nginx가 api로 가는 요청을 대신 처리하고 있는 상태가 된 것이다. 결과적으로 origin이 같아져 CORS이슈는 발생하지 않는다.

 

 

 

 

 

 


참고자료

https://beomy.github.io/tech/browser/cors/

https://escapefromcoding.tistory.com/724

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#%EC%B6%9C%EC%B2%98origin_%EB%9E%80?

https://chat.openai.com/


/* 내가 추가한 코드 */ /* 내가 추가한 코드 끝끝 */