ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Security에서 CORS 해결
    spring/security 2021. 3. 24. 15:30

    배경

    한 서버에서 데이터와 정적파일을 같이 렌더링해 사용자의 요청을 처리하는 방식이 아닌 프런트서버와 api서버를 분리하여 프로젝트를 진행했다. 테스트 방식은 먼저 postman을 이용해 올바른 응답을 받는지 테스트하고 그 다음 프런트서버를 띄우고 브라우저를 통해 api서버와 통신하는 테스트를 진행했다. 그런데 api서버의 코드는 전혀 바뀐 바가 없었는데 postman을 이용해 테스트할 때는 정상적인 응답을 하던 서버가 프런트서버를 띄운 뒤 브라우저를 통해 테스트할 때는 오류가 났던 것이다. 파헤쳐보자.

     

     

    테스트 과정

    코드를 보면 알겠지만 localhost:8080/hello로 get요청을 보내면 hello란 스트링을 body에 담아 전달하는 아주 흔한 통신이다. 결과는 당연히 성공이다.

     

    이제 react의 axios를 이용해 동일한 요청을 수행해보자. 버튼 클릭 시 요청이 날아가고, 성공 시 hello를 실패 시 Error를 출력하는 코드다. axios요청에서 body값이 지정된 것도 아니고 딱히 요청이 실패할 이유가 없다. 결과는?

    실패한 요청이 날아오고 api서버에는 어떠한 에러로그도 나오지 않는다. 인텔리J의 디버거를 이용해 HelloController의 hello메소드에 브레이크포인트를 걸어도 그 부분까지 요청이 매핑되지도 않는다.

     

    여기서 크롬의 개발자 도구 콘솔을 통해 원인을 확인할 수 있다.

    localhost:3000의 요청이 localhost:8080/hello로 갔을 때 CORS정책으로 막혔다는 내용을 확인할 수 있다. 추가로 Access-Control-Allow-Origin 헤더가 포함되어 있지 않다는 내용도 나온다. CORS가 무엇인지 자세하게 알아보자

     

     

    CORS 정책

    모질라 문서로 확인해보자. CORS란 HTTP헤더에 기반한 매카니즘이다. 서버가 다른 origin의 브라우저에게자신의 자원이 로드될 수 있도록 헤더에 표시해주는 방법이다. 이 과정을 위해 브라우저는 preflight요청을먼저 보내 자신이 요청할 수 있는 메소드와 origin을 확인한다. 또한 axios와 같이 XMLHttpRequest를 사용한다면 기본적으로 same-origin에 대해서만 브라우저가 올바른 서버로 간주하기 때문에 Access-Control-Allow-Origin헤더가 적절하게 set되어 있어야 한다. 

     

    api서버에 아무런 설정을 하지 않았을 때 응답 헤더는 위와 같고, Access-Control-Allow-Origin헤더를 확인할 수 없다.

     

     

    Spring Security에서 CORS 설정하기

    자세한 동작과정은 생략하기로 하자. 우선 configure메소드를 통해 HttpSecurity에서 cors를 활성화시킨다.이후 cors설정 클래스를 빈으로 등록해주면 api서버는 브라우저의 preflight요청에 적절한 헤더를 추가한 response로 응답할 것이다. 실제 디버깅을 통해 확인해 보자.

    우선 security filters에서 추가한 CorsFilter를 확인할 수 있다.

    CorsFilter의 처리과정을 보면 설정한 processor를 이용해 요청의 유효성을 검증하고 지금 요청이 preflight이라면 요청을 끝내는 처리를 해준다.

    여기서 내부 처리과정을 좀더 살펴보면 CORS조건을 만족한 경우 응답헤더에 Access-Control-Allow-Origin와 함께 요청 도메인을 넣어주는 걸 확인할 수 있다. 이제 브라우저에서 응답헤더를 확인해보자.

    아주 이쁘게 헤더가 추가된 걸 확인할 수 있고 이제 브라우저는 이 헤더를 확인하고야 비로소 본래 요청인 get요청을 보내는 것이다.

     

    한 가지 궁굼한 점이 그렇다면 애초에 axios 요청을 보낼 때의 헤더에 Access-Control-Allow-Origin을 set해서 보내면 어떻게 될까? 헤더의 설정여부와는 관계없이 브라우저는 prefliget요청을 반드시 보내게 되고 서버에서 cors설정이 제대로 되지 않는다면 브라우저의 prefliget요청은 에러를 내게 된다. 즉, 무의미한 설정이 된다.

     

     

    마치며

    문제를 해결했고 브라우저가 서버와 소통하는 방법을 꽤 세심하게 살펴 본 건 맞지만 좀 의문이 든다. preflight을 보내는 대상은 브라우저고 그말은 브라우저가 서버의 자원이 문제가 있는지 확인한다는 뜻이다.만약 서버에서 악의적인 자원을 배포하는 공격자라면 브라우저의 CORS정책을 손쉽게 풀어줄 수 있는 게 아닐까? 반대로 CORS가 서버의 방어책이라면 공격자는 단지 브라우저를 이용하지 않은 요청을 보내면 그만 아닐까? 위에서처럼 postman을 이용해 테스트할 때는 성공했던 것처럼 말이다.

     

    'spring > security' 카테고리의 다른 글

    Spring boot 2.x 와 Spring security Oauth2 연동  (0) 2021.04.21
    SecurityContextHolder의 역할 및 원리  (0) 2021.03.03
    Spring Security 아키텍처  (0) 2021.02.21

    댓글

Designed by Tistory.