이번 포스트에서는 CORS에 대한 심화 내용을 설명한다.
📌 서론
웹 개발을 하다 보면, 다양한 출처(origin)에서 서버로 요청을 보내는 상황이 자주 발생한다. 이때 CORS(Cross-Origin Resource Sharing) 정책이 중요한 역할을 한다. Spring Boot와 React를 예로 들어 CORS에 대해 자세히 알아보도록 하자
1. 요청 헤더와 응답 헤더는 어떻게 다뤄지나?
요청 헤더와 응답 헤더: 기본 원칙
웹 애플리케이션에서 서버로 데이터를 요청할 때 사용하는 '헤더'는 요청 정보를 담고 있다.
- 예를 들어, 로그인 정보나 데이터 형식 등이 여기에 해당된다. 하지만, 요청을 받은 서버가 응답을 줄 때, 이 요청 헤더는 자동으로 응답 헤더로 넘어가지 않는다. HttpServletRequest와 HttpServletResponse는 서로 다른 객체이기 때문이다.
요청 헤더를 응답에 포함시키기
서버가 응답을 줄 때 요청 헤더를 그대로 포함시키고 싶다면, 서버 코드에서 이를 명시적으로 설정해야 한다.
- 예를 들어, 로그인 시 사용되는 'Authorization' 헤더는 요청에만 사용되고, 응답에는 기본적으로 포함되지 않는다. 이걸 응답 헤더에 추가하려면 아래와 같이 코드를 작성해야 한다.
- 이렇게 하면 응답 헤더에 'Authorization' 값을 포함시킬 수 있지만, 보안상의 이유로 이 방법은 권장되지 않는다. 인증 정보는 민감한 정보니까, 클라이언트로 다시 보내는 건 위험할 수 있다.
// 서버에서 응답을 줄 때 'Authorization' 헤더를 추가하는 예제 코드
public ResponseEntity<String> someControllerMethod(HttpServletResponse response) {
// 'Authorization' 헤더 값을 어딘가에서 가져오는 로직
String authValue = ...;
// 응답 헤더에 'Authorization' 값을 추가
response.setHeader("Authorization", authValue);
// 나머지 비즈니스 로직 처리 ...
// 최종적으로 응답을 반환
return ResponseEntity.ok().body("응답 내용");
}
2. Spring Boot에서 보안 헤더 추가하기
SpringBoot의 필터로 보안 헤더 추가하기
- Spring Boot의 필터를 사용해 보안 헤더를 추가할 수 있다. 이는 웹 애플리케이션의 보안을 강화하는 데 도움이 된다.
예를 들어, XSS 공격을 방지하기 위한 X-XSS-Protection이나, MIME 타입 스니핑을 방지하는 X-Content-Type-Options: nosniff 등이 있다.
예시 코드
- 웹 애플리케이션에서 사용자의 브라우저를 통해 XSS 공격을 방지하기 위해 Content-Security-Policy 헤더를 설정하는 상황을 생각해 보자. 이 헤더는 웹 페이지에서 실행될 수 있는 스크립트의 출처를 제한하여, 악의적인 스크립트가 실행되는 것을 방지한다.
- 이 헤더는 매우 중요하고 복잡할 수 있으니, 정책을 설정할 때 주의해야 한다.
// 필터를 통해 Content-Security-Policy 헤더를 추가하는 예시
httpServletResponse.setHeader("Content-Security-Policy", "script-src 'self' https://trustedscripts.example.com");
3. CORS 설정: React와 Spring Boot API의 상호작용
React와 Spring Boot API의 상호작용
React 애플리케이션(예: www.react.com)에서 Spring Boot API(예: www.api.com)로 데이터를 요청할 때, 브라우저는 CORS(Cross-Origin Resource Sharing) 정책에 따라 요청을 처리한다.
- 만약 Spring Boot 서버가 "www.react.com"에서 오는 요청을 허용하도록 설정되어 있다면, React 개발자는 별도의 CORS 처리를 신경 쓸 필요가 없다. 사용자가 React 애플리케이션의 버튼을 클릭하면, 브라우저는 API 서버로 HTTP 요청을 보내는데, 이때 브라우저는 자동으로 현재 페이지의 출처인 "www.react.com"을 Origin 헤더에 담아서 요청과 함께 보낸다.
HTTP 요청 예시
예를 들어, 사용자가 프로필 정보를 불러오는 버튼을 클릭했다고 치자. 그러면 브라우저는 아래와 같은 HTTP 요청을 생성한다.
- "Origin: https://www.react.com" 헤더가 출처 정보를 나타낸다. 이 정보를 바탕으로 Spring Boot 서버는 요청이 설정된 CORS 정책에 부합하는지 확인하고, 적절한 응답을 브라우저로 보내게 된다.
GET /profile HTTP/1.1
Host: www.api.com
Origin: https://www.react.com
...
SpringBoot CORS 설정 예시 코드
- 이제 Spring Boot에서 CORS 설정을 어떻게 하는지 코드 예시를 보자. 이 예시는 Spring Boot 애플리케이션의 WebConfig 클래스에 CORS 정책을 설정하는 방법이다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
// '/profile' 경로에 대해 www.react.com 도메인에서 오는 요청을 허용
registry.addMapping("/profile")
.allowedOrigins("https://www.react.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true); // 쿠키를 포함시킬 경우 true로 설정
}
}
- addMapping은 "/profile" 경로에 대해 "www.react.com" 도메인에서 오는 요청을 허용하도록 한다.
- 그 다음 allowedMethods를 통해 GET, POST, PUT, DELETE 같은 HTTP 메서드를 허용하고, allowedHeaders("*")로 모든 헤더를 허용한다.
- 마지막으로 allowCredentials(true)는 요청에 쿠키나 인증 토큰 같은것들을 포함할 수 있게 설정하는 것이다.
이렇게 설정하면, www.react.com에서 오는 "/profile" 경로로의 요청은 Spring Boot 서버에 의해 허용되고, 브라우저는 API 응답을 React 애플리케이션에게 전달할 수 있게 되는 것이다.
4. 다른 도메인에서의 요청 처리
CORS 정책에 의한 요청 차단
- 만약 "www.testreact.com"라는 도메인에서 "www.api.com"의 API를 호출하려고 할 때, "www.react.com"만 허용하는 Spring Boot 설정이 작성되어 있다면, "www.testreact.com"의 요청은 CORS 정책에 의해 차단될 것이다. 이는 서버가 응답을 보내도 브라우저가 이를 거부한다는 뜻이다.
CORS 정책 변경을 통한 해결 방법
- 이 문제를 해결하기 위해선 서버(SpringBoot)의 CORS 정책을 변경해야 한다.
- "www.testreact.com"을 허용 목록에 추가하거나, 개발 환경에서만 모든 출처를 허용하는 설정을 적용할 수 있다.
프록시 서버를 이용한 우회 방법
- 또 다른 방법으로는 프록시 서버를 설정해서 모든 요청을 "www.react.com"과 같은 허용된 도메인을 통해 우회시키는 것이다. 이 방법은 개발 중에 자주 사용된다.
보안 고려사항
- 하지만 이런 설정을 할 때는 보안을 주의해야 한다. 민감한 데이터가 노출될 위험이 있으니까, 필요한 도메인만 허용하고, 환경별로 다른 CORS 정책을 적용하는 게 좋다. 특정 API 경로에 대해서만 더 엄격한 CORS 정책을 적용하는 것도 고려해 보도록 하자.
5. 결론
CORS에 대한 결론
- CORS(Cross-Origin Resource Sharing)는 웹 보안의 중요한 부분이며, 주로 브라우저에서 실행되는 보안 메커니즘이다. 서버는 CORS 정책에 관계없이 요청을 받고 처리할 수 있지만, 브라우저는 서버의 응답을 검사하여 CORS 정책을 준수하는지 확인한다.
- 만약 서버의 응답이 요청한 출처(예: 웹 페이지의 도메인)에 대해 적절한 CORS 헤더를 포함하고 있지 않다면, 브라우저는 응답에 포함된 데이터에 대한 접근을 자바스크립트에게 차단한다.
이 과정은 다음과 같이 이루어진다.
브라우저의 요청
- 웹 페이지에서 다른 출처의 리소스를 요청할 때(예: React 앱에서 Spring Boot API로의 요청), 브라우저는 자동으로 Origin 헤더를 요청에 포함시켜 서버로 보낸다.
서버의 응답
- 서버는 요청을 처리하고 응답을 생성할 때, Access-Control-Allow-Origin 헤더를 포함시킬 수 있다. 이 헤더는 요청한 출처가 자신의 리소스에 접근할 수 있는지를 나타낸다.
브라우저의 검사
- 응답을 받은 브라우저는 Access-Control-Allow-Origin 헤더를 확인한다.
- 만약 이 헤더가 요청한 출처를 허용하거나 와일드카드(*)를 사용해 모든 출처를 허용한다고 선언하고 있다면, 자바스크립트는 응답 데이터를 사용할 수 있다. 그렇지 않으면, 브라우저는 자바스크립트가 응답에 접근하는 것을 차단하고, 보안 위반을 나타내는 오류를 콘솔에 기록한다.
이러한 과정은 웹 애플리케이션의 보안을 강화하는 데 중요하다. 사용자의 데이터를 보호하고, 악의적인 사이트가 사용자가 신뢰하는 다른 사이트의 데이터를 읽는 것을 방지하기 위해서다. 그래서 개발할 때 CORS 설정을 신중하게 해야 하고, 특히 공개 API를 제공할 때는 더욱 그렇다.
📌 마무리
SpringBoot에서 React의 요청을 받았을 때 CORS설정에 따라서 서버가 어떻게 동작할지 알아봤다.
이걸 공부하게 된 계기는 이전 포스트에서 Filter를 공부하던 도중에 필터의 보안처리 파트에서 다이어그램 설명 도중 "add security header"라는 부분이 있었는데 이곳에서 어떤 식으로 보안 헤더를 넣을지 알아보다가 여기까지 오게 되었다. 덕분에 많은 공부를 했고 결국에 이런 식으로 공부에 공부를 이어가게 되는 선순환이 되어 기뻤다. 시간이 된다면 한번 Filter글을 보고와도 좋을 것 같다.
2023.11.04 - [Spring 기초/Filter&Interceptor] - Spring - Filter완전 정복: 웹 애플리케이션의 효율성과 보안 높이기
'Spring 기초 > Spring 기초 지식' 카테고리의 다른 글
[Spring] JAR와 WAR 이해하기 (0) | 2023.11.10 |
---|---|
Spring Boot: 필터에서 doFilter와 FilterChain이란? (2) | 2023.11.05 |
Spring: 필터(Filter)가 인터셉터(Interceptor)와 다른점 (1) | 2023.11.04 |
[Spring] 스프링과 자바의 동시성과 병렬 처리 (1) | 2023.10.31 |
Spring: @ControllerAdvice와 AOP를 함께 사용하여 에러 로깅하기 (0) | 2023.10.24 |