cors 에러는 웹개발에서 계속 발생하고, 곤란하게만든다. 무슨이유로 에러가 발생하는가. 어떻게 해결할 것인가?
CORS 에러는 SOP라는 브라우저의 원칙으로 인해 발생한다.
SOP는 Same Origin Policy로, 동일 출처가 아닌 경우 발생한다.
여기서 동일 출처는 프로토콜,호스트,포트가 동일한 경우이다.
그렇다면 동일 출처가 아닌 경우 브라우저가 접근을 차단하는 이유는 뭘까?
사용자가 웹사이트에 접근할 때는 브라우저의 쿠키에 로그인 세션 토큰을 남기게 된다.
해당 토큰을 사용해 사용자는 매번 로그인할 필요없이 서비스를 이용할 수 있다.
웹사이트는 토큰을 받아 파싱, 해석하여 로그인되었음을 인지한다.
그러나 로그인 토큰이 남아있는 브라우저에서,
사용자가 악성사이트에 접속한 경우, 악성사이트는 사용자 브라우저의 토큰에 접근할 수 있다.
악성사이트가 이 토큰으로 네이버와 같은 웹사이트에 로그인을 요청할 경우, 사용자는 악의적인 상황에 놓일 수 있다.(탈탈,, 털려)
이런 상황에서 브라우저는 SOP에 따라 이런 악의적 상황을 막습니다.
브라우저는 악성 사이트의 요청서버와, 요청받고 응답을 주는 응답서버의 출처가 다른걸 보고 CORS에러를 내립니다.
웹사이트 서버에서 악성사이트를 허가해주지 않는 이상, 악성사이트는 CORS에러로 정상적으로 로그인할 수 없습니다.
따라서 사용자가 보는 피해를 방지할 수 있습니다.
👿 악의적인 상황
1. 내가 A 사이트에 로그인하면 브라우저에 ID등 세션정보(토큰)를 쿠키에 저장함
2. 그 상황에서 우연히 특정 악성 사이트에 접근하게된다.
3. 악성사이트는 브라우저에게 시켜 내 쿠키를 읽어 토큰을 갈취함
4. 악성사이트에서 A 사이트에 내 세션 토큰으로 요청해 로그인하고 나쁜짓함
😇 브라우저의 SOP에 의한 해결
1. 악성사이트에 대한 A사이트의 응답을 분석한다.
2. 두 사이트의 출처가 다르다면 악성사이트에게 CORS 에러 내림
3. 악성사이트는 나쁜짓 못한다.
CORS란 Cross origin resource sharing으로,
이런 sop로 발생하는 에러를 해결해주는 일종의 방법입니다.
sop로 사용자를 보호할 수 있지만,
서버는 점차 확장되고, 때로는 다른 회사의 서버부를 이용해야(API 사용 등등) 하는 상황이 자주 발생합니다.
따라서 조건에 따라 이런 다른 출처를 허가해줘야 합니다.
요청만으로 서버에 영향을 주지 않는 단순요청의 경우,
1. 서버에게 본요청을 전달한다.
2. 서버가 OK랑 Access-Control-Allow-Origin를 보낸 경우 응답을 전달한다.
3. Access-Control-Allow-Origin이 안오면 본요청이 fail되고 브라우저는 CORS에러를 발생시킨다.
단순요청이 아닌경우, 예비요청을 통해 허가를 받은 후, 본 요청을 할 수 있습니다.
1. OPTIONS 메소드로 Origin(호스트 uri)을 실어서 보낸다. (예비 요청)
2. 서버가 요청을 허가할 경우 OK랑 Access-Control-Allow-Origin을 보낸다.
3. 브라우저는 서버의 허가를 확인 후 본 요청을 보내게 된다.
CORS는 크게 두 부분으로 해결할 수 있습니다.
1. 프론트엔드의 웹서버에서 요청을 받아서 백엔드서버로 요청을 보내는 것
2. 백엔드 서버에서 자체적으로 요청을 허가해주는 것
우선 cors에러는 브라우저를 통과하며 발생합니다.
따라서 온전한 서버끼리의 통신에서는 발생하지 않습니다.
프론트엔드 단의 웹서버에서 요청을 프록시 형태로 감싸 백엔드 서버로 바로 요청할 경우, 에러가 발생하지 않습니다.
백엔드 서버에서 Access-Control-Allow-Origin 응답을 설정하게 하여, 임의로 허가를 내려줄 수 있습니다.
static class AppConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedMethods("GET", "POST") //메소드 지정 가능
.allowedOrigins("*"); //와일드 카드: 전체 다 허락
}
}
@Controller
//특정 컨트롤러에만 CORS 적용하고 싶을때.
@CrossOrigin(origins = "*", methods = RequestMethod.GET)
public class customController {
@GetMapping("/url")
//특정 메소드에만 CORS 적용 가능
@CrossOrigin(origins = "*", methods = RequestMethod.GET)
@ResponseBody
public List< > findAll(){
return service.getAll();
}
}