CORS
SOP
CROS를 설명하기 앞서, SOP에 대한 개념을 먼저 알고 넘어가겠습니다.
SOP는 Same Origin Policy의 약자로, 다른 출처의 리소스를 사용하는 것에 제한하는 보안 방식 입니다.
Protocol과 Host, Port가 모두 일치하는 경우에 출처가 같다고 표현합니다.
어느 하나라도 다른 경우 다른 출처(Origin)이게 되는 것입니다.
예를 들면, http://localhost와 동일한 출처를 한번 보겠습니다.
- https://localhost -> 프로토콜이 다르므로 X
- http://localhost:80 -> 일치 O
- http://127.0.0.1 -> 브라우저 입장에서 String Value를 비교하기 때문에 서로 달라서 X
- http://localhost/api -> protocol, host, port가 일치하므로 O
그렇다면 왜 SOP가 필요할까요?
페이스북 로그인을 할 경우 브라우저에서 facebook.com 으로 요청을 보내서 사용자는 페이스북 인증 토큰을 가져옵니다.
이때 해커가 사용자에게 클릭을 유도하는 메일을 보냅니다.
사용자가 메일에 있는 http://hakcer.ck 링크를 클릭하니 원치않는 특정 작업을 시도하게 됩니다.
http://hacker.ck 에서 사용자의 토큰을 사용해 facebook.com으로 요청을 보냅니다.
이 시점에서 SOP가 발생합니다.
페이스북 입장에서는 facebook.com의 출처에서 요청이 들어와야 하는데 hacker.ck에서 요청이 들어왔기 때문에 SOP에 위반되어 해당 요청을 거부합니다.
만약 다른 출처의 리소스가 필요할 때 사용하는 것이 CORS입니다.
CORS
CORS란 Cross-Origin Resource Sharing의 약자로, 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다.
CORS를 구현하는데는 3가지 방식이 있습니다.
프리플라이트 요청(Preflight Request)
Preflight는 실제 요청을 보내기 전에 서버에 사전에 가능한지 물어보는 방식입니다.
Preflight로 실제 요청을 보내기 전에 사전 요청을 보내고 서버에서 가능하다고 답변이 오면 그때서야 실제 요청을 보내게 됩니다.
Preflight의 요청의 경우, OPTIONS 메서드로 보내게 되고 위와 같은 내용들이 담기게 됩니다.
서버 측에서는 위와 같은 응답값을 보내게 됩니다.
Preflight 방식의 경우, 결국 사전과 실제 요청으로 2번의 요청이 보내지기 때문에 매번 2번의 요청이 오고가면 리소스적으로 좋지 않으므로 캐싱해두고 다음 요청에 대해서는 캐시 기간동안은 1번만 요청이 오고가게 됩니다.
단순 요청(Simple Request)
단순 요청 방식은 Preflight요청 없이 바로 요청을 보내는 방식입니다.
단순 요청 방식은 다음 조건을 전부 만족해야 합니다.
- GET, POST, HEAD 메서드
- Content-Type
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
- 헤더는 Accept, Accept-Language, Content-Language, Content-Type만 허용
단순 요청 방식이 더 간단해보이는데 Preflight를 쓰는 이유
CORS를 모르는 서버를 위해서 Preflight방식이 필요합니다.
서버가 CORS에 대해 모르는 경우, 일단 요청에 대해 모든 수행을 마치고 응답을 보내게 되는데, 브라우저는 ALLOW-ORIGIN 관련 내용이 응답에 없기 때문에 사용자에게 CORS 에러라고 보내게 됩니다.
하지만 이미 서버에서는 모든 동작이 수행되었기 때문에 문제가 발생합니다.
인증정보 포함 요청(Credentialed Request)
인증 관련 헤더를 포함할 때 사용하는 요청입니다.
쿠키, JWT 토큰을 클라이언트에서 담아서 보내고 싶을 때, credentials를 include하게 되면 서버측에서는 Access-Control-Allow-Credentials를 true로 줘야 합니다.
또한, Access-Control-Allow-Origin은 *를 사용하면 안되고 정확하게 명시해줘야 합니다.
CORS 해결책
프론트 프록시 서버 설정하기
실제 브라우저에서 요청은 프론트 서버로 가게 됩니다.
그럼 프론트 서버에서는 요청에 따라 다른 출처로 보내도록 변경하도록 합니다.
CORS는 결과적으로 브라우저에서 터지게 되므로 브라우저 입장에서는 Same Origin 이기 때문에 에러가 터지지 않습니다.
Spring에서 설정하기
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // CORS를 적용할 URL 패턴
.allowedOrigins("*") // 공유를 허락할 Origin 명시
.allowedMethods("GET", "POST") // 허용할 메서드
.maxAge(3000); // 캐싱 시간
}