SOP(Same-origin policy)
어떤 오리진에서 불러온 문서나 스크립트가 다른 오리진에서 가져온 리소스와 상호작용하는 것을 제한하는 보안방식이다. 이는 자바스크립트 보안 정책중에 하나이다.
이 SOP때문에 다른 오리진을 사용하면 corss domain 이슈가 발생한다. 이 이슈를 해결하기 위해 가장 많이 사용하는 방법으로는 1. JSONP, 2. Reverse Proxy, 3. Flash socket이 있다. 그 후 W3C에서 cross domain 이슈를 해결하기 위한 권장사항으로 CORS를 발표했다.
CORS
CORS는 HTTP헤더 기반의 매커니즘이다. 이 매커니즘은 서버가 로딩을 허용한 리소스 외의 어떠한 다른 오리진을 나타내게 하는 매커니즘이다. 또 한 CORS는 브라우저가 corss-origin 리소스를 호스팅하는 서버에 사전 확인 요청을 하는 매커니즘에 의존해 서버가 실제 요청을 허용하는지 확인다. 이 사전 확인 요청에서 브라우저는 HTTP 메서드와 실제 요청에서 사용할 헤더를 포함하는 헤더를 사용한다.
Cross-origin 예시
https://domain-a.com에서 제공하는 프론트엔드 자바스크립트는 XMLHttpRequest를 사용해 https://domain-b.com/data.json에 대한 요청을 만든다.
보안상의 이유료 브라우저들은 스크립트에서 초기화된 cross-origin HTTP요청을 제한한다. 예를 들어 XMLHttpRequest와 Fetch API는 same-origin policy를 따른다. 즉 이러한 API들을 사용하는 웹 애플리케이션은 애플리케이션이 올바를 CORS헤더를 포함한 다른 오리진에서 온 응답으로 인해 로드되지 않는 한 같은 오리진에서온 리소스만 요청할 수 있다.
CORS 메커니즘은 보안 cross-origin요청을 지원하고 데이터는 브라우저와 서버사이를 오간다. 현대의 브라우저들은 API안에서 CORS를 사용한다.(XMLHttpRequest, Fetch등).
What requests use CORS?
cross-origin shargin 표준은 cross-site HTTP 요청을 다음과 같은 것들을 위해 허용할 수 있다.
1. XMLHttpRequest 또는 Fetch API의 호출
2. 웹 폰트
4. drawImage()의 의해 그려진 이미지/비디오 프레임들
Functional overview
cross-origin recourse 표준은 일을 새로운 HTTP헤더들을 추가함으로써 공유한다. 이 헤더들은 서버들이 어느 오리진들이 웹 브라우저에서 정보를 읽도록 허용하는지를 서술하게 한다. 부가적으로, 서버 데이터에 부작용을 야기할 수 있는 HTTP 요청의 경우 명세서는 브라우저가 이런 요청을 사전에 검증하도록 한다. 서버에게 HTTP OPTIONS 요청 메서드로 지원하는 메서드들을 요청한다. 그 후 서버가 허용한 메서드가 클라이언트가 원하는 메서드이명 실제 요청을 보낸다. 또한 서버가 클라이언트에게 인증서를 요청에 포함해 보내야 할지를 알릴 수 있다.
CORS장애는 에러를 발생시키지만 보안상의 이유로 자바스크립트에서 에러를 명세하는것은 허용되지 않는다. 모든 코드는 단지 에러가 발생했다는 것만 알고있다. 어떤 부분이 잘못되었는지를 판단할 수 있는 유일한 기준은 브라우저 콘솔에 있는 에러이다.
Examples of access control scnarios
CORS는 다음과 같은 3가지의 방식으로 사용할 수 있다.
1. Simple requests
2. preflighted requests
3. request with credentials
1. Simple requests
Simple reuqest는 다음 조건을 모두 충족하는 요청이다.
1. 다음 중 하나의 메서드: GET, HEAD, POST
2. 유저 에이전트가 자동으로 설정한 헤더 외에 사용가능한 헤더는 오직 Fetch명세에서 CORS-safelisted request-header로 정의한 헤더 뿐이다.
Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-width, Width,
Content-Type.
Content-Type의 경우 다음과 같은 값들만 허용된다: application/x-www-form-urlencoded, multipart/form-data, text/plain
3. 요청에 사용되는 XMLHttpRequestUpload객체에는 이벤트 리스터가 등록되어 있지 않아야한다. 이들은 XMLHttpRequest.upload프로퍼티를 사용한다.
4. 요청에 ReadableStream객체가 사용된다.
예시:
https://foo.example의 웹 컨텐츠가 https://bar.other도메인의 컨텐츠를 호출하길 원한다 하자. 여기서 foo.example이 다음과 같은 자바스크립트 코드를 사용한다면
브라우저는 시작줄의 URL에 /resources/publicdata/를 넣고 헤더에 Origin: https://foo.example를 추가해 요청을 보낸다.
서버는 이에 대한 응답으로 헤더에 Access-Control-Allow-Origin를 포함해 응답을 전송한다. 만약 Access-Control-Allow-Origin:*이면 모든 도메인에 대해 접근을 허용한다는 의미이고 *대신 https://foo.example이 적혀 있다면 이 도메인에 한해서 접근을 허용한다는 의미이다.
2. Preflighted requests
preflighted request의 경우 유저 OPTIONS 메서드를 통해 다른 도메인의 리소스로 HTTP 요청을 보내서 실제 요청을 보내기에 안전한지 확인을 하고 안전하다면 요청을 보낸다.
다음은 preflight 요청을 할때의 자바스크립트 파일과 요청응답이다.
브라우저는 위의 자바스크립트 코드 스니펫이 사용중인 요청 파라미터를 기반으로 전송을 해야 서버가 실제 요청 파라미터로 요청을 보낼 수 있는지를 판단할 수 있다.
preflight request에서:
Access-Control-Request-Method는 실제 요청을 보낼때 사용되는 메서드를 기술한다.
Access-Control-Request-Headers는 실제 요청에서 어떤 헤더를 전송할지를 알려준다.
preflight response에서:
Access-Control-Allow-Origin은 CORS를 허용하는 도메인을 나타낸다
Access-Control-Allow-Methods는 실제 요청에서 허용하는 메서드를 나타알려준다
Access-Control-Allow-Headers는 실제 요청에서 허용하는 헤더를 알려준다
Access-Control-Masx-Age는 다른 preflight request를 조내지 않고도 preflight request에 대한 응답을 캐시할 수 있는 시간을 제공한다. 단위는 초이며 이 값이 클수록 각 브라우저의 최대 캐싱 시간의 우선순위가 높다.
3. Requests with credentials
XMLHttpRequest나 Fetch는 인증 정보(credentialed) 요청을 사용한다. credentialed request는 HTTP cookies와 HTTP Authentication정보를 인식한다. cross-site XMLHttpRequest나 Fetch 호출에서 브라우저는 자격 증명을 보내지 않는다. XMLHttpRequest객체나 Request생성자가 호출될때 특정 플래그를 설정해야한다.
http://foo.example에서 불러온 컨텐츠는 쿠키를 설정하는 http://bar.other 리소스에 simple GET request를 작성한다. foo.example는 다음과 같은 자바스크립트를 포함한다.
invocation.withCredentials는 쿠키와 함께 호출하기 위한 XMLHttpRuequest의 플래그를 보여준다. withCredentials는 불리안 값을 갖고 기본적으로는 쿠키 없이 호출이 이루어진다. 이는 simple GET request이기 때문에 preflighted되지 않는다. 하지만 브라우저는 Control-Allow-Credentials:true헤더가 없는 응답을 거부한다. 따라서 호출된 웹 컨텐츠에 응답을 제공하지 않는다.
다음은 클라이언트와 서버의 통신 예시이다.
요청에서 Cookie: pageAccess=2는 http://bar.other의 컨텐츠를 대상으로 하는 쿠키가 포함되있다. 이때 응답에서 Access-Control-Allow-Credentials: true 헤더를 사용하지 않으면 응답은 무시되고 웹 컨텐츠는 제공되지 않는다.
3.1 Credentialed request and wildcards
credentialed request에 응답할때 서버는 Access-Control-Allow-Origin 헤더에 와일드카드('*')대신에 URL을 반드시 지정해야한다.
만약 이 헤더의 값이 와일드 카드이면 요청이 실패한다.
Third-party cookies
CORS응답에 설정된 쿠키에는 일반적인 third-pary cookie정책이 적용된다.
CORS에서 사용되는 요청, 응답 헤더에 대한 설명은 아래 링크를 참고하자