CORS 정책이란, 특정 헤더를 통해서 브라우저에게 한 출처(origin) 에서 실행되고 있는 웹 애플리케이션이 다른 출처(cross-origin)에 원하는 리소스에 접근할 수 있는 권한이 있는지 없는지를 알려주는 매커니즘이다.
참고로 HTTP 요청에 대해서 HTML은 기본적으로 Cross-Origin 요청이 가능하지만, Script 태그 내에 있는 HTTP 요청에 대해서는 Same-Origin 정책을 따르고 있기 때문에 불가능하다.
서버의 위치를 의미하는 https://google.com
과 같은 URL들은 마치 하나의 문자열 같아 보여도, 사실은 아래처럼 여러 구성 요소로 이루어져있다.
만약 https://google.com:443
과 같이 출처에 포트 번호가 명시적으로 포함되어 있다면 이 포트 번호까지 모두 일치해야 같은 출처라고 인정된다.
하지만 이 케이스에 대한 명확한 정의가 표준으로 정해진 것은 아니기 때문에, 더 정확히 이야기하자면 어떤 경우에는 같은 출처, 또 어떤 경우에는 다른 출처로 판단될 수도 있는 것이다.
두 URL의 구성 요소 중 Scheme
, Host
, Port
3가지만 동일하면 된다.
예를 들어, https://ticket-together.com:80
라는 출처는 1)https://
이라는 스킴에 2)ticket-togther.com
호스트를 가지고 3) :80번
포트를 사용하고 있다는 것만 같다면 나머지는 전부 다르더라도 같은 출처로 인정되는 것이다.
하지만 중요한 사실은 CORS는 🌟브라우저의 구현 스펙에 포함되는 정책이기 때문에, 브라우저를 통하지 않고 서버 간 통신을 할 때는 이 정책이 적용되지 않는다. (서버 로그에는 정상적으로 응답했다는 기록만 남음)
먼저, CORS Preflighted requests 란 클라이언트가 HTTP 요청을 서버로 보내기전에, 🌟OPTIONS 메서드를 통해서 다른 도메인의 리소스로 HTTP 요청을 보내 이 요청을 보내도 되는지 확인을 하는 과정을 의미한다.
1) Preflight 요청
OPTIONS
메서드를 사용하며, 서버로 다음과 같은 두가지 헤더를 보내 원래 보내려고 했던 요청을 보내도되는지 확인한다. Origin: 원래 보내려고 했던 요청의 Origin
Access-Control-Request-Method: 원래 보내려고 했던 요청의 HTTP METHOD(ex. PUT, DELETE)
Access-Control-Request-Headers: 원래 보내려고 했던 요청의 Request Headers
어떤 것들을 허용하고, 어떤 것들을 금지하고 있는지에 대한 정보를 담아서 응답한다.
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
흐름을 다시 설명해보자면 다음과 같다.
1) 브라우저에게 리소스를 받아오라는 명령을 내리면 브라우저는 서버에게 예비 요청을 먼저 보낸다.(Preflight Request)
2) 서버는 이 예비 요청에 대한 응답(Preflight Response)으로 현재 자신이 어떤 것들을 허용하고, 어떤 것들을 금지하고 있는지에 대한 정보를 응답 헤더에 담아서 브라우저에게 다시 보내주게 된다.
3) 이후 브라우저는 자신이 보낸 예비 요청과 서버가 응답에 담아준 허용 정책을 비교한 후, 이 요청을 보내는 것이 안전하다고 판단되면 같은 엔드포인트로 다시 본 요청을 보내게 된다.
4) 이후 서버가 이 본 요청에 대한 응답을 하면 브라우저는 최종적으로 이 응답 데이터를 자바스크립트에게 넘겨준다.
서버에게 본 요청부터 보낸 후, 서버가 이에 대한 응답의 헤더에 Access-Control-Allow-Origin
과 같은 값을 보내주면 그때 브라우저가 CORS 정책 위반 여부를 검사하는 방식이다.
이 요청은 HTTP Method와 HTTP Header가 아래 조건을 충족해야한다. 해당 조건을 충족하지 않으면, simple request가 아니라 판단하여 preflighted requests 를 날린다.
HTTP Method
GET, POST, HEAD
HTTP HEADER
ACCEPT
Accept-Language
Content-Language
Content-Type
application/x-www-form-urlencoded
multipart/form-data
text/plain
와일드카드인 *
을 사용하여 이 헤더를 세팅하게 되면 모든 출처에서 오는 요청을 받아먹겠다는 의미이므로 당장은 편할 수 있겠지만, 이상한 출처에서 오는 요청까지 받아드리겠다는 의미로 보안적으로 심각한 이슈가 발생할 수도 있으니 허용 출처를 정확히 명시하도록 하자.
해당 라이브러리가 제공하는 프록시 기능을 사용하면 아주 편하게 CORS 정책을 우회 가능하다.
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'https://api.evan.com',
changeOrigin: true,
pathRewrite: { '^/api': '' },
},
}
}
}
출처
https://taes-k.github.io/2019/12/05/spring-cors/
https://siosio3103.medium.com/cors-이젠-끝내보자-e4cedce3d1c
https://evan-moon.github.io/2020/05/21/about-cors/