CORS?
웹 애플리케이션에서 HTTP 프로토콜을 통해 데이터를 요청하고 응답을 받는다.
즉, 웹 사이트는 URL이라는 출처를 가지고 요청을 하게 되는데, 동일 출처
에서 불러온 문서나 스크립트가 다른 출처에서 가져온 리소스와 상호작용 하는 것을 제한하는 보안 방식을 SOP(Same Origin Polict)라 한다.
이는 악의를 가진 사용자가 정보를 탈취하거나 잠제적인 악성 문서를 격리하기 위해 브라우저에서 사용되는 방식이다.
그러나 웹 이라는 오픈스페이스 환경에서 다른 출처에 있는 리소스를 사용하는 일이 빈번하게 발생하게 되고 무작정 막을 수 없기 때문에 CORS(Cross Origin Resource Sharing) 라는 보안 방식이 등장하게 되었다.
동일 출처
란 두 URL의 프로토콜, 포트(명시한 경우), 호스트가 모두 같은 출처이다.
SOP(Same Origin Policy)
- 다른 출처의 리소스를 사용하는 것을 제한하는 보안 방식.
-
XMLHttpRequest
,Fetch API
처럼 JavaScript로 서로 다른 도메인에 대한 요청은 SOP 정책을 따른다.
CORS(Cross Origin Resource Sharing)
- 다른 출처의 자원을 공유하는 보안 방식.
- 교차 출처 리소스 공유(CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다. 즉, 우리가 가져오는 리소스들이 안전한지 검사하는 관문이다.
-
link
,script
,img
,video
,audio
,iframe
태그 및@font-face
처럼 교차 삽입의 경우 CORS 정책을 따른다.
정리하자면 CORS 정책이 적용되는 네트워크 요청의 경우, 네트워크 요청(request)에는 현재 요청을 보내는 사이트의 Origin
헤더가 존재해야 하고, 네트워크 응답(response)에는 그에 상응하는 Access-Control-Allow-Origin
헤더가 존재해야 한다.
결과적으로 요청의 Origin
헤더와 응답의 Access-Control-Allow-Origin
헤더에 명시된 도메인이 일치해야만 네트워크 요청이 정상적으로 마무리되고 우리가 원했던 리소스를 가져올 수 있게 된다. -> CORS 이슈는 SOP에 의해 발생한다.
항상 이론을 이해하면서 고개를 끄덕이지만 실제로 접하면...
CORS 동작에 따른 3가지 시나리오
위에서 설명한 바와 같이 CORS는 다음과 같은 과정을 가진다.
클라이언트에서 HTTP 프로토콜을 사용하여 요청을 보내게 되는데 이 때 헤더에
Origin
으로 요청의 출처를 담아서 함께 보낸다.서버에서
Access-Control-Allow-Origin
에 요청의 출처를 담아 클라이언트로 응답을 보낸다.클라이언트에서 응답을 받으면 브라우저가
Origin
과Access-Control-Allow-Origin
을 비교하여 SOP를 위반했는지 확인한다.
이러한 동작은 아래의 3가지 시나리오에 의해 다시 달라진다.
- Preflight Request
- Simple Request
- Credentialed Request
Preflight Request
브라우저가 예비요청과 본 요청으로 나누어 서버로 전송하게 되는데, 이 예비요청을 Preflight 라고 한다.
- 실제 요청이 전송하기 안전한지 확인하기 위해 실제 요청 이전에 수행하는 사전 요청이다.
- 서버는 응답 헤더에
Access-Control-Allow-Origin
값을 전달하여 브라우저가 SOP 위반 여부를 판단하게 한다. Access-Control-Allow-Origin: * | (origin)
- OPTIONS 메서드를 사용하여 사전 요청을 보낸다.
- 일반적으로 CORS가 발생하는 요청이다.
Simple Request
명확한 명칭은 없지만 Preflight Request를 보내지 않고 서버에게 요청을 하는 것을 단순 요청(Simple Request)라고 한다.
하지만 아무 때나 단순 요청을 사용할 수 있는 것은 아니고, 특정 조건을 만족하는 경우에만 예비 요청을 생략할 수 있다. 게다가 이 조건이 조금 까다롭기 때문에 일반적인 방법으로 웹 어플리케이션 아키텍처를 설계하게 되면 거의 충족시키기 어려운 조건들이라 필자도 이런 경우를 거의 경험하지는 못 했다.
- 사전 요청을 보내지 않고 바로 서버에 요청을 보낸다.
- 서버는 응답 헤더에
Access-Control-Allow-Origin
값을 전달하여 브라우저가 SOP 위반 여부를 판단하게 한다. Access-Control-Allow-Origin: * | (origin) | null
-
Access-Control-Allow-Origin: <origin>
인 경우 Vary 속성도 같이 제공해야 한다. MDN 참고
Simple Request는 아래 조건을 전부 만족해야 요청이 가능하다.
- GET, HEAD, POST 메소드 중 하나를 사용
- Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width 를 제외한 헤더를 사용하면 안된다.
- Content-Type 사용 시 application/x-www-from-urlencoded, multipart/form-data, text/plain 만 허용
Credentialed Request
Credentialed Request은 요청 시 보안을 강화하고 싶을 때 사용하는 방법이다.
일반적으로 XMLHttpRequest, fetch는 별도 옵션 없이 브라우저의 쿠키 정보, 인증과 관련된 헤더를 요청에 넣지 않지만, Credentialed Request를 통해 헤더에 인증과 관련된 정보를 넣을 수 있다.
이때 요청에 인증과 관련된 정보를 담을 수 있게 해주는 옵션이 바로 credentials
옵션이다.
credentials 옵션 3가지
- same-origin : 기본값이며, 같은 출처 간 요청에만 인증 정보를 담게 한다.
- include : 모든 요청에 인증 정보를 담는다.
- omit : 모든 요청에 인증 정보를 담지 않는다.
same-origin
이나 include
와 같은 옵션을 사용하여 리소스 요청에 인증 정보가 포함된다면, 이제 브라우저는 다른 출처의 리소스를 요청할 때 단순히 Access-Control-Allow-Origin
만 확인하는 것이 아니라 Access-Control-Allow-Credentials: true
를 서버에서 응답 헤더에 포함하여 전달해 주어야 한다.
- 기존 CORS 방식에서 인증 관련 헤더를 포함할 때 사용하는 요청이다.
- 요청 헤더에
credentials
옵션을 사용하게 되면 인증 관련된 헤더를 넣을 수 있다. - 서버는 응답 헤더에
Access-Control-Allow-Origin : *
값을 사용할 수 없으며, 명시적인 URL 이어야 한다. - 서버는 응답 헤더에 반드시
Access-Control-Allow-Credentials : true
가 있어야 클라이언트의 인증 포함 요청에 허용이 가능하다.
ref: https://evan-moon.github.io/2020/05/21/about-cors/
ref: https://beomy.github.io/tech/browser/cors/
Top comments (0)