교차 출처 리소스 공유(Cross-Origin Resource , CORS)는 추가 HTTP 헤더를 사용하여 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처(프로토콜, 도메인, 포트번호)의 리소스에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다. - mdn 설명
출처중 하나라도 다를 경우에 출처가 다른 교차 출처(Cross-Origin)라고 판단되며 브라우저에서는 보안 때문에 Cross-Origin HTTP 요청을 제한한다. 권한을 부여 받기 위한 Cross-Origin 요청은 서버의 동의가 필요한는데 HTTP-header를 통해서 받을 수 있다.
출처(프로토콜, 도메인, 포트번호) 세가지만 동일한지 확인하면 되는데 모든 출처가 일치한다면 Same Origin, 하나라도 일치하지 않는다면 Cross Origin이 된다
프로토콜 - http와 https는 프로토콜이 다르다.
도메인 - domain.com과 other-domain.com은 다르다.
포트 번호 - 8080포트와 3000포트는 다르다.
출처(Origin) 라는 것은 Protolcol 과 Host 그리고 Port 까지 모두 합친 URL을 의미한다고 보면 된다.
Protocol(Scheme) : http, https
Host : 사이트 도메인
Port : 포트 번호
Path : 사이트 내부 경로
Query string : 요청의 key와 value값
Fragment : 해시 태크
CORS가 없이 모든 곳에서 데이터를 요청할 수 있게 되면, 다른 사이트에서 원래 사이트를 흉내낼 수 있게 된다. 예를 들어서 기존 사이트와 완전히 동일하게 동작하도록 하여 사용자가 로그인을 하도록 만들고, 로그인했던 세션을 탈취하여 악의적으로 정보를 추출하거나 다른 사람의 정보를 입력하는 등 공격을 할 수 있습니다. 이렇나 공격을 할 수 없도록 브라우저에서 보호하고, 필요한 경우 에만 서버와 협의하여 요청할 수 있도록 하기 위해서 필요하다.
SOP(Same Origin Policy) 정책은 동일한 출처에서만 리소스를 공유할 수 있다.
동일 출처(Same-Origin) 서버에 있는 리소스는 자유로이 가져올수 있지만, 다른 출처(Cross-Origin) 서버에 있는 이미지나 유튜브 영상 같은 리소스는 상호작용이 불가능하다는 말이다.
https://www.domain.com:3000 출처에 대한 여러 URL에 따른 동일 출처 비교 표
URL | 출처 구분 | 이유 |
---|---|---|
https://www.domain.com:3000/about | 동일 출처 | 프로토콜, 호스트, 포트 번호 동일 |
https://www.domain.com:3000/about?username=inpa | 동일 출처 | 프로토콜, 호스트, 포트 번호 동일 |
http://www.domain.com:3000 | 다른 출처 | 프로토콜 다름 (http ≠ https) |
https://www.another.co.kr:3000 | 다른 출처 | 호스트 다름 |
https://www.domain.com:8888 | 다른 출처 | 포트 번호 다름 |
https://www.domain.com | 다른 출처 | 포트 번호 다름 (443 ≠ 3000) |
브라우저는 요청을 보낼때 한번에 바로 보내지않고, 먼저 예비 요청을 보내 서버와 잘 통신되는지 확인한 후 본 요청을 보낸다.
예비 요청의 역할은 본 요청을 보내기 전에 브라우저 스스로 안전한 요청인지 미리 확인하는 것이다
이때 브라우저가 예비요청을 보내는 것을 Preflight라고 부르며, 이 예비요청의 HTTP 메소드를 GET이나 POST가 아닌 OPTIONS라는 요청이 사용된다는 것이 특징이다.
이때 내용물은 없이 헤더만 전송한다.
정리
Simple requests가 아닌 cross-origin요청은 모두 preflight 요청을 하게 되는데, 실제 요청을 보내는 것이 안전한지 확인하기 위해 먼저 OPTIONS 메서드를 사용하여 cross-origin HTTP 요청을 보냅니다. 이렇게 하는 이유는 사용자 데이터에 영향을 미칠 수 있는 요청이므로 사전에 확인 후 본 요청을 보낸다.
OPTIONS 메소드로 예비요청을 보내 보안을 강화하는것은 좋지만 실제 요청에 걸리는 시간이 늘어나게 되어 어플리케이션 성능에 영향을 미치는 단점이 있다.
브라우저 캐시(Cache) 를 이용해 Access-Control-Max-Age 헤더에 캐시될 시간을 명시해 주면, 이 Preflight 요청을 캐싱 시켜 최적화를 시켜줄 수 있다.
예비 요청 캐시는 다른 캐싱 매커니즘과 유사하게 작동한다
3가지 경우를 만족 할때 만 예비 요청을 생략
까다로운 조건들이 많기 때문에 대부분의 API 요청은 그냥 예비 요청(preflight)으로 이루어진다 라고 이해하면 된다.
인증된 요청은 클라이언트에서 서버에게 자격 인증 정보(Credential)를 실어 요청할때 사용되는 요청이다.
자격 인증 정보란
세션 ID가 저장되어있는 쿠키(Cookie) 혹은 Authorization 헤더에 설정하는 토큰 값 등을 일컫는다.
1. 클라이언트에서 인증 정보를 보내도록 설정하기
- 요청에 인증과 관련된 정보를 담을 수 있게 해주는 옵션이 바로 credentials 옵션이다. 이 옵션에는 3가지의 값을 사용할 수 있으며, 각 값들이 가지는 의미는 아래와 같다.
옵션 값 | 설명 |
---|---|
same-origin(기본값) | 같은 출처 간 요청에만 인증 정보를 담을 수 있다. |
include | 모든 요청에 인증 정보를 담을 수 있다. |
omit | 모든 요청에 인증 정보를 담지 않는다. |
- 만일 이러한 별도의 설정을 해주지 않으면 쿠키 등의 인증 정보는 절대로 자동으로 서버에게 전송되지 않는다.
2. 서버에서 인증된 요청에 대한 헤더 설정하기
서버도 마찬가지로 이러한 인증된 요청에 대해 일반적인 CORS 요청과는 다르게 대응해줘야 한다.
응답의 Access-Control-Allow-Origin 헤더가 와일드카드(*)가 아닌 분명한 Origin으로 설정되어야 하고, Access-Control-Allow-Credentials 헤더는 true로 설정되어야 한다는 뜻이다. 그렇지 않으면 브라우저의 CORS 정책에 의해 응답이 거부된다. (인증 정보는 민감한 정보이기 때문에 출처를 정확하게 설정해주어야 한다)
- preflight요청에 대한 대한 응답으로 실제 요청 시 사용할 수 있는 HTTP 헤더를 나타낸다.
참고
https://hannut91.github.io/blogs/infra/cors
https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F
https://hymndev.tistory.com/78