ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Boot - CORS 설정
    Framework & Library/Spring Boot 2023. 3. 20. 15:34

    CORS

    CORS란?

    CORS(Cross-Origin Resource Sharing)는 출처가 다른 자원들을 공유한다는 뜻으로, 한 출처에 있는 자원에서 다른 출처에 있는 자원에 접근하도록 하는 개념이다. 직역하자면, 교차되는 출처 자원들의 공유이다. 다른 출처에 있는 자원을 요청한다고 하면, 이를 교차 출처 요청이라고 한다.

    CORS는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다. 웹 애플리케이션은 리소스가 자신의 출처와 다를 때 교차 출처 HTTP 요청을 실행한다.

     

    출처란?

     

    먼저 출처라는 개념을 알아야 한다. 위 사진은 URL이 어떤 구성요소로 이루어져 있는지 나타내고 있다. 위 구성요소 중에서 Protocol + Host + Port 3가지가 같으면 동일 출처라고 한다.

     

    동일 출처 예시

    http://Example.com:80
    http://example.com
    HTTP 기본 Port인 80번이 생략되었기 때문에, 두 URL은 동일 출처이다.
    http://example.com/app1/index.html 
    http://example.com/app2/index.html

     Protocol, Host, Port가 같으며, Path부터 다르므로 동일 출처이다.

     

    교차 출처 예시

    http://example.com/app1 
    https://example.com/app2
    Protocaol이 다르다.
    http://example.com 
    http://www.example.com
    http://myapp.example.com
    Host가 다르다.
    http://example.com
    http://example.com:8080
    80, 8080으로 Port가 다르다.

     

    위에서 두 번째와 같이 다른 출처의 요청일 경우, CORS 정책에 준수하여 요청해야만 정상적으로 응답을 받을 수 있다.

     

    교차 출처 요청의 위험성

    <img>, <script>, <frame>, <video>, <audio> 등이 웹에 등장하면서, 페이지 로딩 이후에 브라우저에서 이러한 하위 자원들을 가져올 수 있게 되었다. 이에 따라, 동일 출처, 교차 출처 모두 호출이 가능하게 되었다.

     

    CORS 정책이 없고 다른 출처 요청이 가능한 브라우저를 생각해 보겠다.

     

     

    홈페이지를 서핑하고 있는데, <script>가 심어진 evil.com 페이지를 열었다고 생각해 보겠다. 굉장히 유용한 정보를 담고 있는 사이트지만, 페이지를 열면서 <script>가 실행되어 은행에 'Delete/account'를 요청하도록 되어있다. AJAX을 통해 은행 API를 호출하여 나의 은행 계좌를 삭제해 버리는 사고가 발생한다.

     

    위와 같은 문제 때문에, 다른 출처의 접근을 막기 위해서 동일 출처 정책이 등장하게 되었다.

     

    동일 출처 정책(Same-origin policy)

    동일 출처 정책(Same-origin policy)은 다른 출처로부터 조회된 자원들의 읽기 접근을 막아 다른 출처 공격을 예방한다. 그러나, 다른 출처에서 얻은 이미지를 담는 <img>, 외부 주소를 담는 <link> 같은 여러 태그들을 허용한다. 동일 출처 정책의 정확한 구현 명세는 없지만 최신의 브라우저들은 일정 규칙을 따르고 있다.

     

     

    동일 출처 정책은 다른 출처 자원을 가져오는 것을 굉장히 제한적으로 허용했다. 또한, SPA와 미디어 중심 웹 사이트들이 더욱 늘어나고 있으므로 관련 규칙들도 계속 늘어나고 있다. 따라서, 다른 출처 리소스에 접근성을 높이기 위해서 CORS가 등장했다.

     

    동일 출처 요청 vs 교차 출처 요청

    동일 출처 요청과 교차 출처 요청이 어떻게 다른지 그림을 통해서 비교해 보겠다.

     

    요청하는 클라이언트와 요청받는 서버가 같은 출처에 있으면 동일 출처, 서로 다른 서버에 있으면 교차 출처 요청이다.

     

     

    왼쪽 스마트폰의 URL은 domain-a.com이다. 그리고 오른쪽 서버의 URL은 domain-a.comdomain-b.com 두 가지이다.

    domain-a.com 유저가 domain-a.com 서버에 요청하면 동일 정책이기 때문에 아무런 문제가 없지만, domain-a.com 유저가 domain-b.com 서버에 요청하면 Host가 다르기 때문에 교차 출처 요청을 하게 된다.

    기본적으로는 동일 출처 요청만 자유롭게 요청이 가능하며, 동일 출처 정책(Same-Origin Policy)이라고 한다. 하지만, 기준을 완화하여 다른 출처 요청도 할 수 있도록 기준을 만든 체제가 교차 출처 정책(Cross-Origin Policy)이라고 한다.

     

    Spring Boot에서 Cross-Origin 설정하기

    Spring Boot에서 Cross-Origin을 설정하는 방벙에 대해 알아보겠다. 메서드 설정, 컨트롤러 설정이 있으며 개별적으로도 적용이 가능하다.

     

    01. 메서드에 설정하기

    @RestController
    @RequestMapping("/account")
    public class AccountController {
        
        @CrossOrigin
        @RequestMapping(method = RequestMethod.GET, path = "/{id}")
        public Account retrieve(@PathVariable Long id) {
        }
        
        @RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
        public void remove(@PathVariable Long id) {
        }
    }

    retrieve() 메서드에 선언된 @CrossOrigin의 기본 설정은 아래와 같다.

     

    1. 모든 출처가 허용된다.

    2. 허용된 HTTP 메서드는 @RequestMapping으로 선언된 메서드들이다.

     

    02. 컨트롤러에 설정하기

    @CrossOrigin(origins = "http://example.com", maxAge = 3600)
    @RestController
    @RequestMapping("/account")
    public class AccountController {
    	
        @RequestMapping(method = RequestMethod.GET, path = "/{id}")
        public Account retrieve(@PathVariable Long id) {
        }
        
        @RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
        public void remove(@PathVariable Long id) {
        }
    
    }

    컨트롤러 상단에 @CrossOrigin 애너테이션을 설정했기 때문에, AccountController에 있는 retrieve() 메서드와 remove() 메서드 모두에 적용된다.

     

    03. 개별 설정하기

    @CrossOrigin(maxAge = 3600)
    @RestController
    @RequestMapping("/account")
    public class AccountController {
    
        @CrossOrigin("http://example.com")
        @RequestMapping(method = RequestMethod.GET, "/{id}")
        public Account retrieve(@PathVariable Long id) {
        }
    
        @RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
        public void remove(@PathVariable Long id) {
        }
    }

     

    스프링에서는 위 코드와 같이 여러 가지 CORS 정책을 복합해서 설정할 수 있다. 

    모든 메서드들은 캐시 시간이 3600초이며, retrieve() 메서드는 허용 출처가 http://example.com 밖에 되지 않는다. 하지만, remove() 메서드는 별도의 설정이 없기 때문에, 모든 출처가 가능하다.

     

    04. 전역 설정하기

    @Configuration
    @EnableWebMvc
    public class WebConfig implements WebMvcConfigurer {
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**");
        }
    }

    CORS 정책의 설정은 WebMvcConfigurer 인터페이스를 구현하여 설정할 수 있다. 이는 필터 기반이기 때문에, 전역적으로 모든 요청에 대해서 검사한다.


    출처

    https://www.baeldung.com/spring-cors

    https://www.baeldung.com/cs/cors-preflight-requests

    https://ieftimov.com/posts/deep-dive-cors-history-how-it-works-best-practices/

     

    728x90

    댓글

Designed by Tistory.