CORS(Cross-Origin Resource Sharing)는 브라우저에서 다른 출처의 리소스를 요청할 때 발생하는 보안 정책이다.
브라우저는 기본적으로 동일 출처 정책(SOP)에 따라 같은 출처에서만 리소스를 요청할 수 있지만, CORS를 통해 출처 간 리소스 요청이 가능하다.
SOP는 2011년 RFC 6454에서 등장한 보안 정책으로 "같은 출처에서만 리소스를 공유할 수 있다"
현대 웹 애플리케이션은 외부 API나 서버에서 데이터를 요청해야 할 때가 많다.
예를 들어:
CORS는 이러한 출처 간 리소스 공유를 안전하게 관리할 수 있도록 한다.
CORS를 이해하기 위해서는 Origin(출처)의 개념이 필요한데, Origin은 URL에서 프로토콜, 호스트, 그리고 포트로 구성된다.
예를 들어, https://example.com:443
과 http://example.com:80
은 다른 출처로 간주된다.
같은 출처는 이 세 가지 요소가 모두 동일해야만 인정된다.
CORS는 브라우저에서만 작동하는 정책이기 때문에, 서버는 CORS 위반 여부와 상관없이 요청을 처리하고 응답을 정상적으로 보낸다.
하지만 브라우저가 응답을 분석하여 CORS 정책에 맞지 않다고 판단하면 해당 응답을 사용하지 않고 폐기한다.
이로 인해 서버는 정상적인 로그만 남기지만, 브라우저 측에서는 CORS 오류가 발생할 수 있다. CORS 정책을 잘 모르면 이와 같은 상황에서 오류 추적이 어려울 수 있다.
브라우저에서 GET, POST, HEAD 메서드와 특정 기본 헤더만 사용한 요청은 Simple Request로 처리된다.
Content-Type이 application/x-www-form-urlencoded
, multipart/form-data
, text/plain
일 때 해당되며, 복잡한 웹 애플리케이션은 이러한 요청을 충족하기 어려운 경우가 많다.
예를 들어, application/json
은 Simple Request에 해당되지 않는다.
또한, 사용자 인증과 관련된 Authorization
헤더를 사용하는 경우 Simple Request로 처리되지 않는다.
대부분의 복잡한 웹 요청은 Preflight Request를 통해 처리된다.
브라우저는 서버에 본 요청을 보내기 전에, 먼저 OPTIONS 메서드를 사용해 예비 요청을 보내는데, 서버는 이 요청에 대해 허용되는 HTTP 메서드(Access-Control-Allow-Methods
), 허용되는 헤더(Access-Control-Allow-Headers
), 허용할 출처(Access-Control-Allow-Origin
) 등을 응답한다.
브라우저는 이 정보를 바탕으로 본 요청을 서버로 전송할지 여부를 결정한다.
이 과정은 서버가 특정 요청을 허용할 수 있도록 미리 검증하는 과정이다.
Preflight Request의 응답 예시:
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
여기서 Access-Control-Max-Age
는 프리플라이트 요청을 캐싱해 일정 시간 동안 예비 요청을 생략할 수 있는 기간을 나타낸다.
이 값이 클수록 브라우저는 서버와 효율적으로 통신할 수 있다.
인증 정보(쿠키, 토큰 등)를 포함한 요청은 Credential Request로 처리된다.
이때 서버는 반드시 Access-Control-Allow-Credentials: true
를 응답해야 하며, Access-Control-Allow-Origin
은 특정 출처(와일드카드 *
허용 불가)로 설정되어야 한다.
이 방식은 사용자 인증과 같은 민감한 요청을 처리할 때 주로 사용된다.
예를 들어, fetch
나 Axios
와 같은 클라이언트 라이브러리를 사용할 때 서버에 쿠키를 전송해야 하는 경우, 클라이언트 요청에 credentials 옵션을 추가하고, 서버가 적절한 응답 헤더를 설정해야 요청이 처리된다.
Credential Request 옵션 값:
same-origin
: 같은 출처 간에만 인증 정보를 전송.include
: 모든 출처에 인증 정보 포함 가능.omit
: 인증 정보를 전송하지 않음.Credential Request의 경우 Access-Control-Allow-Origin
에서 특정 출처를 명시하고, Access-Control-Allow-Credentials: true
가 응답에 반드시 포함되어야 한다.
그렇지 않으면 CORS 정책에 의해 응답 무시
CORS 정책은 브라우저 스펙이다.
Postman 같은 도구는 브라우저를 사용하지 않기 때문에 CORS 문제를 발생시키지 않는다.
즉, Postman이 Cross-Origin을 검사하는 브라우저 보안 규칙을 따르지 않기 때문
CORS 정책에서 예비 요청이 성공하더라도, 본 요청이 실패할 수 있다.
브라우저는 예비 요청에서 성공 응답을 받았다 하더라도 응답 헤더가 적절하지 않으면 여전히 CORS 정책을 위반한 것으로 간주하고, 요청을 처리하지 않는다.
중요한 것은 응답 헤더에 적절한 Access-Control-Allow-Origin
값이 존재하는지다.
정상 응답 예시:
Access-Control-Allow-Origin: https://foo.example
비정상 응답 예시:
Access-Control-Allow-Origin: *
위와 같이 와일드카드 *
가 사용된 경우, 인증이 필요한 요청은 CORS 정책 위반으로 간주된다.
이처럼 CORS는 복잡한 웹 통신을 안전하게 관리하는 데 중요한 역할을 하며,
이를 제대로 이해해야 오류 추적 및 문제 해결이 용이해진다.
출처: https://koguri.tistory.com/65
출처: https://evan-moon.github.io/2020/05/21/about-cors/#%EC%B6%9C%EC%B2%98origin%EA%B0%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80%EC%9A%94
출처: https://www.popit.kr/cors-preflight-%EC%9D%B8%EC%A6%9D-%EC%B2%98%EB%A6%AC-%EA%B4%80%EB%A0%A8-%EC%82%BD%EC%A7%88/