CORS
contents
CORS(Cross-Origin Resource Sharing, 교차 출처 리소스 공유) 는 웹 페이지가 자신을 제공한 도메인(Origin)이 아닌 다른 도메인의 서버에 있는 제한된 리소스에 접근할 수 있도록 허용하는 브라우저 보안 메커니즘입니다.
CORS를 이해하려면 먼저 CORS가 완화시켜 주는 보안 규칙인 동일 출처 정책(SOP, Same-Origin Policy) 을 알아야 합니다.
1. 핵심 문제: 동일 출처 정책 (SOP)
기본적으로 웹 브라우저는 '동일 출처 정책(SOP)'을 강제합니다. 이는 한 페이지(예: evil.com)의 악성 스크립트가 다른 페이지(예: yourbank.com)의 민감한 데이터를 탈취하지 못하도록 막기 위함입니다.
두 URL이 "동일 출처(Same Origin)"로 간주되려면 다음 세 가지가 정확히 일치해야 합니다:
- 프로토콜 (예:
https) - 호스트 (예:
example.com) - 포트 (예:
:443또는:8080)
이 중 하나라도 다르면, 브라우저는 보안상 이유로 스크립트가 응답을 읽는 것을 차단합니다. CORS는 브라우저에게 "이 특정 교차 출처(Cross-Origin) 요청은 안전하니 허용해도 된다"고 알려주는 표준 방법입니다.
2. CORS의 동작 방식
CORS는 요청을 서버로 보내는 것을 막는 필터가 아니라, 서버로부터 돌아온 응답을 브라우저가 읽지 못하게 막는 메커니즘입니다.
브라우저가 다른 출처(Cross-Origin)로 요청을 보낼 때 특정 헤더를 추가합니다. 서버는 이에 대해 적절한 CORS 헤더로 응답해야 합니다. 만약 서버의 헤더가 접근을 허용하지 않는다면, 브라우저는 응답 데이터를 버리고 자바스크립트 에러를 발생시킵니다.
CORS 상호작용에는 크게 단순 요청(Simple Requests) 과 프리플라이트 요청(Preflighted Requests) 두 가지 유형이 있습니다.
A. 단순 요청 (Simple Requests)
표준 HTML form 전송과 유사하여 비교적 "안전하다"고 간주되는 요청들입니다. 다음 조건을 모두 만족해야 합니다:
- 메서드:
GET,HEAD, 또는POST. - 헤더: 브라우저가 자동으로 설정하는 헤더 외에, 수동으로 설정할 수 있는 헤더는
Accept,Accept-Language,Content-Language,Content-Type등으로 제한됨. - Content-Type:
application/x-www-form-urlencoded,multipart/form-data,text/plain중 하나여야 함.
동작 흐름:
- 브라우저:
Origin헤더를 포함하여 요청을 보냅니다 (예:Origin: https://frontend.com). - 서버: 데이터와 함께
Access-Control-Allow-Origin헤더를 응답으로 보냅니다. - 브라우저: 돌아온
Access-Control-Allow-Origin헤더가 요청한 Origin과 일치하는지 확인합니다. 일치하면 자바스크립트가 데이터를 받습니다.
B. 프리플라이트 요청 (Preflighted Requests)
Authorization 같은 커스텀 헤더를 보내거나, PUT, DELETE 메서드를 사용하거나, application/json 타입의 데이터를 보낼 때 발생합니다. 브라우저는 "이 요청은 서버의 데이터를 변경할 수도 있으니, 먼저 허락을 받아야겠다"라고 판단합니다.
동작 흐름:
- 브라우저 (프리플라이트): 실제 요청을 보내기 전에, 자동으로
OPTIONS메서드 요청을 서버로 보냅니다.- 질문 내용: "제가
PUT메서드와Authorization헤더를 사용해도 될까요?"
- 질문 내용: "제가
- 서버:
OPTIONS요청에 대해 200 또는 204 상태 코드와 함께 허용 여부가 담긴 헤더로 응답합니다. - 브라우저: 허락이 떨어지면, 그때 비로소 실제(Actual) 요청을 보냅니다.
3. 핵심 HTTP 헤더
CORS 동작을 제어하는 주요 헤더들입니다.
요청 헤더 (브라우저가 전송)
Origin: 요청을 보내는 출처(도메인)를 나타냅니다.Access-Control-Request-Method: (프리플라이트 전용) 실제 요청에서 어떤 HTTP 메서드를 쓸지 서버에 알립니다.Access-Control-Request-Headers: (프리플라이트 전용) 실제 요청에서 어떤 커스텀 헤더를 쓸지 서버에 알립니다.
응답 헤더 (서버가 전송)
Access-Control-Allow-Origin: 가장 중요한 헤더입니다. 리소스 접근이 허용된 출처를 지정합니다.- 값:
*(모두 허용) 또는https://specific-domain.com(특정 도메인만 허용).
- 값:
Access-Control-Allow-Methods: 허용되는 HTTP 메서드 리스트 (예:GET, POST, OPTIONS, PUT).Access-Control-Allow-Headers: 허용되는 커스텀 헤더 리스트 (예:Authorization, X-Custom-Header).Access-Control-Allow-Credentials:true로 설정되는 불리언 값입니다. 쿠키나 인증 정보를 포함한 요청을 허용할 때 필요합니다.Access-Control-Max-Age: 프리플라이트 요청의 결과를 브라우저가 얼마나 오래(초 단위) 캐시할지 정합니다. 이를 통해 매번OPTIONS요청을 보내는 것을 방지합니다.
4. 자격 증명 (Credentials: 쿠키 및 인증)
기본적으로 교차 출처 요청은 쿠키나 HTTP 인증 정보를 보내지 않습니다.
자격 증명을 포함하려면:
- 클라이언트(JS)에서
withCredentials옵션을true로 설정해야 합니다. - 서버는 반드시
Access-Control-Allow-Credentials: true로 응답해야 합니다. - 중요 규칙:
Access-Control-Allow-Credentials가true인 경우,Access-Control-Allow-Origin헤더에 와일드카드(*)를 쓸 수 없습니다. 반드시 명시적인 도메인(예:https://frontend.com)을 적어줘야 합니다.
5. 흔한 CORS 에러
- "No 'Access-Control-Allow-Origin' header is present": 서버에 CORS 설정이 안 되어 있거나, 서버가 귀하의 도메인을 차단한 경우입니다.
- "The value of the ... header in the response must not be the wildcard '*'": 자격 증명(쿠키 등)을 보내려고 하는데 서버가 와일드카드(
*)로 설정되어 있는 경우입니다. 서버는 요청받은 구체적인 Origin을 똑같이 반환해줘야 합니다. - Preflight 404/403/500: 서버가
GET/POST는 처리하지만OPTIONS메서드를 어떻게 처리해야 할지 모르거나 에러가 나는 경우입니다.
6. 보안 모범 사례 (Best Practices)
- 운영 환경에서 와일드카드(
*) 사용 지양: 편리할 수는 있지만, 모든 사이트가 귀하의 API 응답을 읽을 수 있게 됩니다. 신뢰할 수 있는 도메인만 허용 목록(Whitelist)으로 관리하세요. - Origin 검증: 서버 코드에서
Origin헤더 값을 하드코딩된 문자열과 비교하기보다는, 허용된 도메인 DB나 리스트와 대조하여 동적으로 검증하세요. - 보안을 CORS에만 의존하지 말 것: CORS는 브라우저의 정책입니다. 해커가 Postman이나
curl같은 도구를 써서 브라우저를 거치지 않고 서버에 직접 요청을 보내면 CORS는 무용지물입니다. 따라서 적절한 서버 측 인증(Authentication)과 인가(Authorization)가 필수적입니다.
references