1. CORS란 무엇인가?
CORS(Cross-Origin Resource Sharing)는 웹 브라우저에서 외부 도메인 리소스를 안전하게 요청할 수 있도록 하는 표준 규약입니다. 프론트엔드와 백엔드가 분리하는데 있어 CORS에 대해서 반드시 짚고 넘어가야합니다. 그래서 온르은 CORS에 대해서 공부해보겠습니다.
2. CORS의 필요성
핵심은 외부로부터 리소스를 공유하는 것입니다. 요즘 웹 애플리케이션에 개발에서 백엔드와 프론트엔드를 구분하지 않고 개발하는 곳은 거의 없을 겁니다.
- 프론트엔드와 백엔드의 분리
- 마이크로서비스 아키텍처 도입
- 외부 API 활용
- SPA(Single Page Application) 개발 방식
이러한 상황에서 다른 출처(Origin)의 리소스를 안전하게 요청하고 사용할 수 있어야 했고, 이를 위한 표준이 바로 CORS입니다.
3. Same-Origin Policy
Same-Origin Policy는 웹 브라우저의 기본적인 보안 정책으로, 같은 출처에서만 리소스를 공유할 수 있도록 제한합니다.
출처(Origin)는 다음 세 가지 요소로 결정됩니다:
- 프로토콜 (http, https)
- 호스트 (domain)
- 포트 번호
- http://example.com/path1, https://example.com/path2 는 프로토콜이 다르므로 다른 출처로 간주됩니다.
4. CORS 동작 방식
CORS는 HTTP 헤더를 통해 동작합니다. 주요 헤더는 다음과 같습니다:
4-1. 요청 헤더
- Origin: 요청을 보내는 출처
- Access-Control-Request-Method: 실제 요청에서 사용할 HTTP 메서드
- Access-Control-Request-Headers: 실제 요청에서 사용할 헤더
4-2. 응답 헤더
- Access-Control-Allow-Origin: 허용된 출처
- Access-Control-Allow-Methods: 허용된 HTTP 메서드
- Access-Control-Allow-Headers: 허용된 헤더
- Access-Control-Max-Age: 프리플라이트 요청 캐시 시간
- Access-Control-Allow-Credentials: 인증 정보 포함 여부
5. CORS 요청의 종류
5-1. Simple Request
- GET, HEAD, POST 중 하나의 메서드 사용
- 허용된 헤더만 사용
- Content-Type이 다음 중 하나:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
5-2. Preflight Request
Simple Request 조건을 만족하지 않는 요청의 경우, 브라우저는 실제 요청 전에 OPTIONS 메서드를 사용한 예비 요청을 보냅니다.
5-3. Credentialed Request
인증 정보(쿠키, HTTP 인증)를 포함한 요청입니다.
6. Spring Security CORS 설정
Spring Security CORS 설정에 대해서 알아보겠습니다.
UrlBasedCorsConfigurationSource apiConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
// 허용할 출처(Origin) 설정
// https://api.example.com 에서 오는 요청만 허용
configuration.setAllowedOrigins(List.of("https://api.example.com"));
configuration.setAllowedOriginPatterns(List.of(
"https://*.example.com", // example.com의 모든 서브도메인 허용
"https://*.example.*.com", // 더 복잡한 패턴 매칭도 가능
"http://localhost:[*]" // 로컬호스트의 모든 포트 허용
));
// 허용할 HTTP 메서드 설정
// GET과 POST 메서드만 허용 (PUT, DELETE, PATCH 등은 차단됨)
configuration.setAllowedMethods(List.of("GET","POST"));
// 허용할 헤더 설정
// 모두 허용
configuration.setAllowedHeaders(List.of("*"));
// 클라이언트에게 노출할 헤더
configuration.setExposedHeaders(List.of("Authorization"));
// allowCredentials를 true로 설정할 경우, allowedOrigins에 "*"를 사용할 수 없습니다
configuration.setAllowCredentials(true);
// CORS 프리플라이트 요청의 캐시 시간
configuration.setMaxAge(3600L);
// URL 패턴별로 CORS 설정을 적용할 수 있는 객체 생성
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 모든 경로("/**")에 대해 위에서 설정한 CORS 설정을 적용
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.cors(cors -> cors
.configurationSource(apiConfigurationSource())
);
return http.build();
}
6-1. Preflight 란?
configuration.setMaxAge() 에 대해서 간단하게 알아보겠습니다.
- 브라우저는 실제 요청 전에 OPTIONS 메서드를 사용하여 preflight 요청을 보냅니다
- 이 요청으로 해당 출처가 안전한지, 어떤 메서드와 헤더가 허용되는지 확인합니다
- 매 요청마다 preflight를 보내면 성능 저하가 발생할 수 있습니다
6-1-1. maxAge의 역할
// 예시: preflight 응답 헤더
Access-Control-Max-Age: 3600
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type
- 브라우저가 preflight 응답을 캐시하는 시간을 지정
- 캐시 기간 동안은 동일한 요청에 대해 preflight를 다시 보내지 않음
- 서버 부하 감소와 성능 향상에 도움
6-1-2. 브라우저별 최대 제한 시간
// 브라우저별 최대 캐시 시간이 다름
Chrome: 2시간 (7200초)
Firefox: 24시간 (86400초)
Safari: 7일
6-1-3. 장점
- 서버 부하 감소
- 네트워크 트래픽 감소
- 응답 시간 개선
6-1-4. 단점
- CORS 정책 변경 시 캐시된 정책이 즉시 적용되지 않을 수 있음
- 브라우저마다 다른 최대 제한으로 일관성 있는 동작을 보장하기 어려움
6-2. allowCredentials와 allowedOrigins 설정
allowCredentials를 true로 설정하고 allowedOrigins에 "*"를 함께 사용할 수 없는 것은 중요한 보안상의 이유 때문입니다.
credentials에는 쿠키, HTTP 인증 토큰과 같은 민감한 인증 정보가 포함됩니다. 그런데 allowedOrigins에 모든 도메인("*")의 접근을 허용한다면 악의적인 웹사이트에서 사용자의 인증정보를 이용해 요청을 보낼 수 있게 됩니다.
[CS][Spring Security] CSRF란?
CSRF란?CSRF Cross-Site Request Forgery의 약자로 인증된 사용자의 권한을 악용하여 해당 사용자가 의도하지 않은 요청을 웹사이트에 전송하는 공격 기법입니다. 공격자는 사용자가 이미 인증된 상태를
tmd8633.tistory.com
이에 대해서는 CSRF에 대해서 읽어보시기 바랍니다.
따라서 브라우저는 이러한 보안 위험을 방지하기 위해 allowCredentials(true)와 allowedOrigins("*")의 조합을 명시적으로 금지하고 있습니다. 이는 웹 보안의 기본 원칙인 "최소 권한의 원칙"을 따르는 것이며, 실수로 인한 보안 취약점 발생을 방지합니다.
7. 자주 발생하는 CORS 에러와 해결 방법
7-1. No 'Access-Control-Allow-Origin' header is present
- 원인: 서버에서 Access-Control-Allow-Origin 헤더를 설정하지 않음
- 해결: 서버에서 적절한 CORS 설정 추가
7-2. Method not allowed
- 원인: 허용되지 않은 HTTP 메서드 사용
- 해결: allowedMethods에 필요한 메서드 추가
7-3. Credentials flag is true, but Access-Control-Allow-Credentials is false
- 원인: 인증 정보를 포함한 요청에 대한 서버 설정 미비
- 해결: allowCredentials(true) 설정 추가
8. 보안 관련 고려사항
8-1. Origin 설정
- "*" 대신 구체적인 도메인 지정
- 신뢰할 수 있는 출처만 허용
8-2. 인증 관련
- allowCredentials(true) 사용 시 구체적인 출처 지정 필요
- 보안에 민감한 API의 경우 더 엄격한 CORS 정책 적용
8-3. 헤더 설정
- 필요한 헤더만 허용
- exposedHeaders 설정 시 최소한의 헤더만 노출
8-4. 캐시 설정
- maxAge 값을 적절히 설정하여 불필요한 프리플라이트 요청 감소
9. 결론
CORS는 현대 웹 개발에서 필수적인 보안 메커니즘입니다. 올바른 CORS 설정은 웹 애플리케이션의 보안과 기능성을 모두 만족시킬 수 있습니다. 각 프로젝트의 요구사항과 보안 정책에 맞게 적절한 CORS 설정을 적용하시기 바랍니다.
'일반 > CS' 카테고리의 다른 글
[CS][Spring Security] CSRF란? (0) | 2024.12.12 |
---|---|
Maven Central Repository에 라이브러리 등록하기 (0) | 2024.09.19 |
[CS] MVC 패턴 (0) | 2024.04.17 |
HTTP GET과 POST 차이 (0) | 2024.01.25 |
URI와 URL의 차이점 (Feat : URN) (0) | 2024.01.21 |