참고: https://dev.to/jpomykala/what-is-cors-11kf
이 포스트에서는 CORS와 CORS 에러가 무엇인지, 왜 이런 에러가 발생하는지에 대해 설명하겠습니다. 또한 이를 해결할 솔루션을 제시하고 preflight 요청과 CORS 헤더가 무엇인지, 그리고 이런 것 들이 커뮤니케이션에서 왜 중요한지를 설명할 것 입니다. 이 글은 독자가 웹 개발과 HTTP 프로토콜에 대한 기본적인 지식을 가지고 있다는 가정하에 쓰였습니다. 하지만 초보자도 읽기 쉽도록 CORS 주제와 밀접하게 연관되지 않은 기술적 주제는 피하려고 노력했습니다.
CORS 란?
CORS(Cross-Origin Resource Sharing)는 클라이언트(웹 브라우저)에 의해 제어되는 HTTP 기반 보안 메커니즘입니다. 이를 통해 서비스(API)는 클라이언트가 요청할 수 있는 오리진 이외에 요청할 수 없는 오리진도 표시할 수 있습니다. 한 오리진에서 로드한 웹사이트(HTML 문서 또는 JS 스크립트)가 다른 오리진의 리소스와 상호 작용할 수 없게하는 SOP(Same-origin policy)와 함께 설계되었습니다. CORS는 일부 크로스 오리진 요청을 명시적으로 허용하고 다른 요청은 거부하는데 사용됩니다.
CORS는 기본적으로 웹 브라우저에서 구현되지만 API 클라이언트에서도 옵션으로 사용할 수 있습니다. 현재 Google Chrome, Firefox, Opera 및 Safari와 같은 모든 인기 웹 브라우저에 구현되어 있고 이 표준은 2014년 1월 W3C 권고안으로 승인되었습니다. 이런 이유로 현재 사용 가능한 모든 웹 브라우저에 CORS가 구현되어 있다고 볼 수 있습니다.
CORS의 작동 방법
모든 것은 본격적인 요청이 오고 가기 전 클라이언트 측에서 시작됩니다. 클라이언트는 서비스에게 HTTP 헤더(CORS 헤더)의 매개변수로 CORS preflight 요청을 보냅니다. 그러면 서비스도 같거나 다른 값으로 동일한 응답을 합니다. 클라이언트는 CORS preflight 요청의 결과를 바탕으로 본격적인 요청을 서비스로 보낼지 말지를 결정합니다. 응답이 CORS preflight 요구 사항을 충족하지 않으면 웹 브라우저(클라이언트)는 오류를 발생시킵니다.
CORS preflight 요청은 웹 브라우저가 요청을 보내는 데 사용되는 라이브러리 또는 프레임워크와는 상관없이 전송됩니다. 그렇기 때문에 백엔드 애플리케이션에서는 API 작업을 할 때 CORS 요구 사항을 준수할 필요가 없습니다.
CORS는 사용자가 자원을 요청하거나 다운로드하는 것을 막지는 않습니다. curl, Insomnia, Postman와 같은 앱을 사용하여 리소스를 성공적으로 요청할 수 있습니다. CORS 정책은 브라우저가 CORS 정책을 허용하지 않는 경우에만 리소스에 액세스하지 못하도록 합니다.
CORS preflight 란?
브라우저가 서버에 요청을 발송할 때, 먼저 HTTP OPTIONS 요청을 발송합니다. 이를 CORS preflight 요청이라고 합니다. 그런 다음 서버는 허용된 메소드나 헤더 목록을 응답합니다. 브라우저가 실제 요청을 만들 수 있는 경우 실제 요청을 보냅니다. 그렇지 않다면 오류가 표시되고 메인 요청을 계속 보내지지 않습니다.
CORS 헤더
CORS 헤더는 CORS 정책을 제어하는 데 사용하는 일반 HTTP 헤더입니다. 브라우저가 CORS preflight 요청을 서버로 보내고 서버는 다음과 같은 응답을 합니다.
- Access-Control-Allow-Origin: 리소스를 가져올 수 있는 하나 이상의 오리진을 나타냅니다. (예: https://foo.io,http://bar.io)
- Access-Control-Allow-Methods: 허용되는 HTTP 메소드를 나타냅니다. 콤마로 구분합니다. (예: GET,PUT,POST)
- Access-Control-Allow-Headers: 하나 이상의 허용하는 요청 헤더를 나타냅니다. (예: Authorization,X-My-Token)
- Access-Control-Allow-Credentials: 쿠기 전송이 허용되는지를 나타냅니다. (기본 값: false)
- Access-Control-Max-Age: 요청 결과를 캐시할 시간을 초단위로 나타냅니다. (기본 값: 0)
Access-Control-Allow-Credentials=true를 사용하기로 결정한 경우 Access-Control-Allow-* 헤더에는 와일드카드 *을 사용할 수 없습니다.
CORS 헤더의 전체 목록을 참고하세요.
요청이 CORS 정책에 의해 차단되는 이유는?
웹 개발을 하다보면 CORS 오류에 대해 이미 봤거나 들은 적이 있을 거고 해결책을 찾기 위해 여러 번 검색해 본 적이 있을 것입니다. 가장 일반적인 문제는 브라우저가 CORS 정책 때문에 요청을 차단한다는 것입니다. 브라우저는 오류를 발생시키고 다음과 같이 콘솔에 로그를 표시합니다:
Access to XMLHttpRequest at 'http://localhost:8080/' from origin
'http://localhost:3000' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
위의 CORS 오류는 서버가 접근을 허용하지 않았기에 브라우저 오리진(https://localhost:300)에서 리소스(https://localhost:8080)에 액세스할 수 없음을 사용자에게 알린 것입니다. 서버가 CORS preflight 응답에서 오리진 또는 와일드카드 *로 Access-Control-Allow-Origin 헤더에 응답하지 않았기 때문에 이 문제가 발생했습니다.
서버로의 요청은 잘못된 오리진 뿐만 아니라 잘못된 HTTP 헤더, HTTP 메소드, 쿠키 헤더로인해 CORS 정책에 따라 차단될 수 있습니다.
CORS 에러 해결 방법
"CORS 해결"의 기본 개념은 적절한 헤더를 사용하여 클라이언트에서 보낸 OPTIONS 요청에 응답하도록 하는 것입니다. 올바른 CORS 대응 수단은 여러 가지가 있습니다. 프록시 서버를 사용하거나 서버에 미들웨어를 설치하는 방법 등이 있습니다.
Access-Control-* 헤더는 Access-Control-Max-Age 헤더에 설정된 값에 따라 웹 브라우저에서 캐시되는 부분을 조심해야합니다. 이에 따라 변경 사항을 테스트하기 전에 캐시를 지워야 합니다. 또는 브라우저에서 캐싱을 아예 비활성화할 수도 있습니다.
1. CORS 해결: 서버 설정하기
기본적으로 서버를 수정할 수 있는 경우 서버에서 CORS 응답을 구성해야 하며, 이 방법만이 문제를 올바르게 해결할 수 있습니다. 이는 앱의 여러 계층과 여러 가지 방법으로 수행할 수 있는데, 가장 일반적인 방법은 응답에 헤더를 추가하는 리버스 프록시, API 게이트웨이 또는 기타 라우팅 서비스를 사용하는 것입니다. 여기에 사용할 수 있는 많은 서비스가 있습니다. 예를 들면 HAProxy, Linkerd, Istio, Kong, nginx, Apache, Traefik 등 입니다. 인프라에 추가 계층 없이 애플리케이션만 포함된 경우 애플리케이션 코드에서 CORS 지원을 추가할 수도 있습니다.
다음은 CORS를 활성화하는 몇 가지 예입니다.
- Apache: modify .htaccess file
- Nginx: modify configuration file
- Traefik: use middlewares
- Spring Boot: use @EnableCORS annotation
- ExpressJS: use app.use(cors())
- NextJS: use request helpers
enable-cors.org에서 다양한 프레임워크와 언어로 CORS를 활성화하는 예를 확인할 수 있습니다.
만약 서버를 수정할 수는 없는 상황인데 그래도 CORS를 해결해야 한다면 아래에 설명하는 솔루션 중 하나를 사용해야 합니다.
2. CORS 해결: 브라우저 확장 프로그램을 설치하기
브라우저 확장을 사용하면 클라이언트 개발자 환경에서 CORS 문제를 쉽고 빠르게 해결할 수 있습니다. 이 브라우저 확장을 사용하는 가장 큰 장점은 코드나 구성을 따로 만질 필요가 없다는 것입니다. 반면에 웹 응용 프로그램을 테스트하기 위해 사용하는 모든 웹 브라우저에 확장 프로그램을 설치해야 합니다.
브라우저 확장 프로그램은 브라우저를 속이는 데 필요한 헤더를 추가하여 들어오는 preflight 요청을 변경합니다. 프로덕션 도메인의 요청만 수락하는 프로덕션 API와의 통신을 로컬에서 작업할 수 있게하는 편리한 솔루션입니다.
확장 프로그램은 Google Web Store 또는 Mozilla Add-ons Library에서 찾을 수 있습니다. 경우에 따라 기본 확장 구성이 충분하지 않을 수 있습니다. 설치된 확장이 제대로 구성되었는지 확인하여야 합니다. 또한 확장을 무기한으로 설정하면 일부 웹 사이트에서 문제가 발생할 수 있습니다. 그러므로 이는 개발 목적으로만 사용하는 것이 좋습니다.
3. CORS 해결: 브라우저의 CORS 체크 비활성화
브라우저에서 CORS 검사를 완전히 비활성화할 수 있습니다. Google Chrome에서 CORS 검사를 사용하지 않으려면 브라우저를 닫고 --disable-web-security 및 --user-data-dir 플래그로 시작해야 합니다. 이렇게 하면 Google Chrome은 CORS preflight 요청을 전송하지 않으며 CORS 헤더의 유효성을 검사하지 않습니다.
# Windows
chrome.exe --user-data-dir="C://chrome-dev-disabled-security" --disable-web-security --disable-site-isolation-trials
# macOS
open /Applications/Google\ Chrome.app --args --user-data-dir="/var/tmp/chrome-dev-disabled-security" --disable-web-security --disable-site-isolation-trials
# Linux
google-chrome --user-data-dir="~/chrome-dev-disabled-security" --disable-web-security --disable-site-isolation-trials
위의 모든 명령은 분리된 샌드박스에서 구글 크롬을 시작합니다. 기본 크롬 프로필에는 영향을 주지 않습니다.
Google Chrome 플래그 목록에서 사용 가능한 플래그 목록을 확인할 수 있습니다.
4. CORS 해결: 프록시 서버 만들기
브라우저 설정을 변경하지 않는 솔루션을 찾고 있다면 프록시 서버 솔루션을 살펴봐야 합니다. 이 방법을 사용하면 브라우저 자체를 변경하지 않고도 CORS 오류를 해결할 수 있습니다. 프록시 서버 솔루션의 기본 개념은 모든 요청을 프록시 서버로 보내고 요청을 받은 프록시 서버가 실제 서비스로 요청을 보내는 것 입니다.
프록시 서버는 사용하려는 서비스에 대한 액세스 권한이 없는 경우 쓰기 좋은 솔루션입니다. 오픈소스로 프록시 서버를 구축완료 했다면 이 프록시가 authorization 헤더를 사용해 사용자의 요청을 가로채서 타사 서비스에 전송하려 하는지 항상 확인해야 합니다. 이러한 보안 위반은 개발 업체와 잠재적 서비스 사용자에게 치명적인 문제를 안겨줄 수 있습니다.
인터넷에서 찾을 수 있는 오픈 소스 CORS 서비스 목록:
- https://github.com/Freeboard/thingproxy
- https://github.com/bulletmark/corsproxy
- https://github.com/Rob--W/cors-anywhere
이러한 서비스를 사용하기 전에 항상 코드가 최신 버전인지 확인해야합니다.
CORS 테스트 방법
브라우저를 사용하여 CORS 구성을 테스트하는 것은 지루한 작업이 될 수 있습니다. CORS Tester, test-cors.org 등의 도구를 사용하거나 커맨드라인에 익숙한 경우 curl을 사용하여 CORS 구성을 테스트할 수 있습니다.
curl -v -X OPTIONS https://foo.bar/api/v1/test
거짓 CORS 오류 주의
경우에 따라서 속도 제한 레이어, 로드 밸런서 또는 권한 부여 서버가 뒤에 추가로 있는 경우 잘못된 CORS 오류가 발생할 수 있습니다. 서버에 의해 차단되거나 거부된 요청은 다음의 오류 상태 코드를 확인해야 합니다.
- 401 unauthorized,
- 403 forbidden,
- 429 too many requests,
- 500 internal server error,
- 2XX 이나 3XX 이외의 코드
실패한 preflight 요청으로 인해 요청이 차단된 것 처럼 보일 수도 있는데 실제로는 서비스가 다른 이유로 그냥 거부한 것일 수도 있습니다. 불필요한 디버깅을 방지하기 위해 항상 응답의 상태 코드와 응답 메시지를 확인해야 합니다. 브라우저는 CORS preflight 요청이 실패하면 그대로 경고 메시지를 띄우지만 실패 이유가 반드시 CORS 구성때문이 아닐 수도 있는 점을 유의해야합니다.
정리
이 글에서는 CORS가 무엇이고 CORS의 일반적인 문제는 무엇인지를 설명했습니다. 여기서는 CORS 문제를 해결할 4가지 방법을 제안했고 각각의 장단점을 설명했습니다. 또한 CORS 응답을 올바르게 구성하는 방법과 테스트하는 방법에 대해서도 설명했습니다. 그리고 거짓 CORS 오류가 났을 때 일반적인 문제가 무엇인지 설명했습니다.
참고
'cs' 카테고리의 다른 글
[네트워크] SSL 인증서란? 정의 및 설명 (2) | 2022.12.14 |
---|---|
[네트워크] HTTP - HyperText Transfer Protocol (0) | 2022.12.01 |
[네트워크] 프록시 Proxy (1) | 2022.10.06 |
[네트워크] 패킷 Packet (2) | 2022.09.23 |
[네트워크] NAT - Network Address Translation (3) | 2022.09.20 |
댓글