사이드 프로젝트 여정 제 3탄의 주제는 개발자라면 누구나 한 번 쯤 마주치는 SOP, CORS 에 대해서 이전에 정리한 것 보다 상세하게 정리하고, Spring Boot 에서 어떻게 동작 하는지 어떻게 적용하는지 정리를 해보려고 한다.
1탄 : SSO
2탄 : AWS Amplify
3탄 : CORS && SOP with Spring Boot
먼저, CORS 란 무엇인가?
Cross Origin Resource Sharing 의 약자이다. 한국어로는 교차 출처 리소스 공유이다. 역시 번역은 쓸모없는 경우가 많다.
Origin ? Protocol + Host + Port 를 합쳐서 Origin 이라고 부른다.
ex) http://localhost:8080 은 하나의 Origin 이다.
그렇다면 Cross Origin 은 뭘까? 한국어로 교차 출처라고 하는데, 쉽게 말해서 Origin 이 다를 때 Cross Origin 이라고 한다.
Origin 이 다르다는 의미는 무엇일까?
방금 언급한 Protocol + Host + Port 중 하나라도 다르면 Cross Origin 이다.
ex) http://localhost:8080 과 http://localhost:9090 은 Cross Origin 이다.
ex) http://example.com 과 https://example.com 도 Cross Origin 이다.
ex) https://example.com 과 https://www.example.com 도 Cross Origin 이다.
Cross Origin 이 무엇인지 알아봤는데 리소스 공유는 무엇인가?
web application 스스로 자원을 가지고 있어서 방문자들에게 serving 을 할 수 도있지만, 다른 server 에 있는 자원을 요청해서 보여주거나, 다른 server 에 어떤 자원을 생성하거나 삭제하거나 변경하기 위해서 요청을 할 수도 있다. 다른 server 에 요청할 때 이는 Cross Origin 에게 요청을 하는 것을 의미한다.
따라서 이와 같이 Origin 이 다른 즉, Cross Origin 간에 Resource (자원) 을 공유를 하는 개념이다.
하지만, SOP (Same-Origin Policy) 라는 것이 또 있다.
동일 출처 정책 한국말이다. 번역 역시 단어가 바로 와닿지 않는다. Cross Origin 은 알아봤고, Same Origin 은 Cross Origin 의 반대로 Protocol + Host + Port 가 전부 동일한 경우 Same Origin 이라고 한다.
그렇다면 여기 Policy 가 붙었다. Policy 는 정책이다. 어떤 정책인가?
한 마디로 Cross Origin 간의 자원 문서, 스크립트, 자원에 대한 상호작용을 web browser 에서 제한하겠다는 정책이다.
하지만, 모든 자원에 대해서 제한하는 것은 아니다. iframe, img, script, video, link, object, embed, form tag 들과 같이 embedded tag 는 cross-origin resources 가 허용된다. 하지만 cross-origin read 에 대해서 허용되는 것은 또한 아니다. 무슨 차이인가? embedded 의 의미는 resource 를 read 하는 것이 아니라, cross-origin 의 자원들은 local 에서 copy 한다는 개념으로 이해하면 된다. 반면에, read 는 해당 자원을 보유하는 개념으로 embedded 의 개념과 다르다.
그렇다면 왜? 제한하는 것일까?
CSRF 이슈를 해결하기 위해서 정책이 수립되었다. Cross-Site Request Forgery 의 약자이다.
예시로, 악의적인 사람이 https://naber.com ( v 가 아닌 b ) 라는 도메인을 사용해서 iframe 을 사용하여 https://naver.com 을 띄웠을 때, 일반 사용자는 도메인을 알아채지 못한 채 naver 에 로그인을 할 것이다. 그리고 악의적인 사이트 naber.com 은 naver.com 에 로그인한 사용자의 권한을 통해 사용자가 의도하지 않은 행동들을 할 수 있게 하는 보안적인 이슈가 발생한다.
따라서, 이와 같은 보안 이슈를 다루기 위해서 수립된 정책이며 browser security feature 이다.
SOP 로 인해서 제한적인데, CORS 어떻게 가능하게 하는것일까?
서버에서 허용하는 cross-origin 에 대한 설정을 할 수 있다. CORS 요청은 Simple 혹은 Preflight 를 통해서 가능하다.
Simple Request ?
GET, HEAD, POST 3가지 HTTP Method 를 사용하고 동시에 Content-Type 이 text/plain, application/x-www-form-urlencoded, multipart/form-data, application/json 등 표준이고 동시에 custom header 가 없을 경우 Preflight 없이 Simple request 를 통해서 가능하다. Simple request 의 경우, Browser 는 요청이 시작되는 origin 을 명시하는 Origin header 를 요청에 추가한다. 그리고 서버가 해당 요청을 받고 Browser 에게 CORS 요청이 유효한지 확인하고, Access-Control-Allow-Origin header 로 응답한다.
아래와 같이 실제 Request Message 와 Response Message 를 통해서 확인해보자.
Request Message 에서 HTTP Method 가 GET 이고, Content-type 이 application/json 으로 표준이다. 그리고 또한 custom header 를 사용하지 않았다. 그리고 Browser 에서 요청 시 origin Header 가 추가된 것을 확인할 수 있다.
또한 Response Message 에서 Access-Control-Allow-Origin Header 값에 허용되는 Origin 값이 작성되어 있으며, Access-Control-Allow-Credentials Header 값이 작성되어 있음을 확인할 수 있다.
Browser 가 HTTP response 를 받으면, Browser 는 Request 의 Origin Header 값과 Response Access-Control-Allow-Origin Header 값이 일치하는지 확인하다. 일치하지 않는다면 즉시 response 는 정상적으로 작동하지 않고 SOP 에 따라 Blocked 된다.
Preflight ?
앞서 언급한 Simple Request 의 경우가 아니라면, 전부 Preflight 이다. Preflight 의 경우, 본래의 Request Message 를 보내기전에 Browser 에서 사전에 본래 Request Message 를 Server 에서 받을 수 있는 지 한 번 확인하는 과정이다.
이번에도 실제 Request Message 와 Response Message 를 통해서 확인하자.
먼저 OPTIONS HTTP Method 를 사용하고 Origin Header 가 역시 추가되었다.
또한, Access-Control-Request-Headers Header 와 Access-Control-request-Method Header 가 추가되어 DELETE HTTP Method 와 Authorization Header 그리고 Content-type Header 사용이 가능한 지 Server 에게 확인하는 Preflight 요청을 보낸다.
이에 Server 는 해당 Request Message 가 가능하다고, 200 OK 로 Status code 로 응답하고, Access-Control-Allow-Credentials, Access-Control-Allow-Headers, Access-Allow-Methods, Access-Control-Allow-Origin, Access-Control-Max-Age Header 들을 통해서 Browser 에게 알려주었다.
이제 Browser 는 본래의 Request 인 DELETE 요청을 서버에게 보낸다. 이제 부터는 Simple Request 와 동일하게 Origin Header 를 추가하고 요청을 진행한다. 그리고 정상적으로 처리되고 Server 는 요청에 대한 응답을 한다.
지금까지 Browser 에서 CORS, SOP 가 어떻게 동작 하는지 간략하게 정리해보았다.
지금부터는 Spring Boot 로 구현된 Server 에서 어떻게 CORS 설정을 하는지 간략하게 정리해보겠다.
Spring Boot 에서 CORS Filter 를 Bean 으로 등록해주면 된다. 하지만 Spring Security 를 사용할 경우 Security Filter Chain Proxy 에 등록해주어야 한다는 차이점이 있다.
아래는 Spring Security 설정을 발췌한 부분이다. Security Filter Chain Proxy 에 등록해준다.
CorsConfiguration 객체를 생성하고 AllowedOrigin 에 허용할 Origin 들을 추가해준다.
그리고 허용할 header 들을 추가해준다. 앞서 언급했던 custom header 로 이해할 수 있다.
그리고 허용할 HTTP Method 들도 설정해주고, MaxAge 를 설정해주고, path pattern 을 지정해준다.
그리고 HttpSecurity cors() 를 통해서 등록해주면 설정이 끝난다.
아래는 Spring Security 를 사용하지 않고 일반 Filter chain 에 등록해준다.
이번 글은 여기서 마무리하겠다. 계속해서 사이드 프로젝트와 관련된 내용을 연재하도록 하겠다.
댓글
댓글 쓰기