CORS란?
CORS는 Cross-Origin Resource Sharing의 약자로 직역하면 "교차 출처 리소스 공유" 이다.
좀 더 쉽게 말하면 동일한 출처가 아닌 다른 출처에서 데이터를 주고 받는 것을 허용하는 정책이다.
이미지 출처 : https://antstudy.tistory.com/259
CORS가 없이 모든 곳에서 데이터를 요청할 수 있게 되면, 다른 사이트에서 원래 사이트를 흉내낼 수 있게 된다.
예를 들어서 기존 사이트와 완전히 동일하게 동작하도록 하여 사용자가 로그인을 하도록 만들고, 로그인했던 세션을 탈취하여 악의적으로 정보를 추출하거나 다른 사람의 정보를 입력하는 등 공격을 할 수 있다.
공격을 할 수 없도록 브라우저에서 보호하고, 필요한 경우 에만 서버와 협의하여 요청할 수 있도록 하기 위해서 필요하다.
그렇다면 이러한 정책이 왜 필요한 것일까?
그건 먼저 "동일 출처 정책"에 대해서 알아야 한다.
서버의 위치를 의미하는 https://google.com과 같은 URL 들은 마치 하나의 문자열 같아 보여도,사실은 여러 개의 구성 요소로 이루어져 있다.
(Restful Api와 연결되는 부분이다.)
즉, 서버의 위치를 찾아가기 위해 필요한 가장 기본적인 것들을 합쳐놓은 것이다.
URL의 Protocl, Host, Port를 통해 같은 출처인지 다른 출처인지 판단할 수 있다.
--> Protocol, Host, Port 무엇이라도 다르면, 다른 출처라고 판단한다.
출처 내의 포트 번호는 생략이 가능한데, 이는 각 웹에서 사용하는 HTTP, HTTPS 프로토콜의 기본 포트 번호가 정해져 있기 때문이다.
http://localhost와 동일 출처인 url은 무엇일까?
그러면 http://127.0.0.1는?
- 127.0.0.1 은 localhost가 맞기는 한데 브라우저 입장에서는 String value를 서로 비교한다.
=> 결국 String value가 다르기 때문에 브라우저는 다른 출처로 본다.
보통 프론트엔드 개발자가 React와 같은 라이브러리를 사용해서 개발하는 경우 백엔드 서버와 별도의 프론트 서버가 존재한다.
프론트 개발을 할때, 로컬의 백엔드 서버에 연동하거나 개발 서버에 연결해서 API 연동을 하는데 만일 다음과 같이,
프론트 서버의 URL이 http://localhost:3000이고, 백엔드 서버가 http://localhost:8080에 띄워져 있다고 하면
이때 프론트 서버와 백엔드 서버는 다른 출처 (Origin)으로써 Same-Origin Policy 정책을 어긋나기 때문에, 서버로부터 응답이 넘어올 때 브라우저에서 CORS Policy 오류를 발생시키게 된다.
출처: https://inpa.tistory.com/entry/WEB-📚-CORS-💯-정리-해결-방법-👏 [👨💻 Dev Scroll:티스토리]
=> 이때 필요한 게 CORS이다.
Cross-Origin Resource Sharing의 줄임말이다.
그렇다면 CORS는 어떻게 안전하게 다른 출처와 리소스를 공유하는 것일까?
바로 두 가지 방법이 있는데, 단순 요청 방법과 예비 요청 방법이 있다.
요청 방법은 요청하는 헤더와 응답하는 헤더를 통해서 이루어 진다.
Access-Control-Request-Method
-요청을 할 때 어떤 메서드를 사용할 것인지를 알려주는 것이다.
Access-Control-Request-Headers
-요청을 할 때 어떤 헤더를 사용할 것인지 알려주는 것이다.
Access-Control-Allow-Origin
-리소스에 접근할 수 있도록 허용하는지를 알려주는 것이다.
Access-Control-Expose-Headers
-브라우저에게 접근할 수 있는 리스트들을 알려주는 것이다.
Access-Control-Max-Age
-캐싱되는 시간을 알려주는 것이다.
Access-Control-Allow-Credentials
-크레덴셜이 true일 때 요청할지에 대한 것을 알려주는 것이다.
Access-Control-Allow-Methods
-허용되는 메서드를 알려주는 것이다.
Access-Control-Allow-Headers
-사용 가능한 HTTP 헤더를 알려주는 것이다.
예비 요청은 말 그대로 미리 요청을 보내보고, 안전한지를 판단한 뒤에 본격적으로 요청을 하는 방식이다.
아래와 같이 예비 요청을 보내고 그에 대한 응답을 받은 뒤 실제 요청을 보내고 응답받게 된다.
조금 더 구체적으로 말하면 헤더에 Access-Control-Request-Method를 통해 요청하는 HTTP 메서드 GET,POST,PUT,DELETE 중 하나의 메서드와 Access-Control-Request-Headers를 통해 OPTIONS라는 헤더를 넣고 요청을 보낸다.
이 때 예비로 확인하는 것 뿐이기 때문에 바디에 아무것도 작성하지 않고 헤더만 보낸다.
해당 메서드와 헤더가 유효하다면 서버는 응답 헤더를 통해 접근 가능한지(Access-Control-Allow-Origin), 사용할 수 있는 리소스의 리스트(Access-Control-Expose-Headers), 캐싱 되는 시간(Access-Control-Max-Age) 등을 알려주는 것이다.
단순 요청은 위의 예비 요청과는 달리 서버에 바로 본격적으로 요청을 시작한다.
그렇다면 미리 확인하지도 않고 어떻게 안전하게 리소스를 요청하는 것일까?
그 방법은 조금 까다로운 조건들을 거는 것이다.
코드 적용
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry corsRegistry) {
corsRegistry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.allowedHeaders("*")
.exposedHeaders("*")
.exposedHeaders("Authorization","Refresh-Token")
.maxAge(3000);
}
@Slf4j
"@CrossOrigin"
@RequiredArgsConstructor
@RestController
public class CGVInfoController {
//private static Logger logger = LoggerFactory.getLogger(CGVInfoController.class);
private final CgvInfoService cgvInfoService;
...
참고 : https://developer.mozilla.org/ko/docs/Web/HTTP/CORS
참고 : https://antstudy.tistory.com/259
참고 : https://fomaios.tistory.com/entry/Network-CORS%EB%9E%80-feat-%EB%B3%B4%EC%95%88HTTP
굉장히 복잡한데 CSRF,XSS를 공부하면서 복잡한 만큼 우리에게 반드시 필요하니까 사용하는 것이다.
꾸준히 읽고 이해하고 공부하자.