
브라우저는 인증 정보로 사용될 수 있는 쿠키를 브라우저 내부에 보관한다.
이후 이용자가 웹 서비스에 접속할 때, 브라우저는 쿠키를 HTTP 요청에 포함시켜 전달한다.
이러한 동작은 사이트에 직접 접속하는 것에만 한정되지 않으며, 웹 리소스를 통해 간접적으로 타 사이트에 접근할 때도 쿠키를 함께 전송한다. 이 때문에 악의적인 페이지는 클라이언트의 권한을 이용하여 사용자에게 HTTP 요청을 보내도록하여 HTTP 응답 정보를 획득 한다.
따라서, 클라이언트는 가져온 데이터를 악의적인 페이지에서 읽을 수 없도록 해야한다. 이를 위한 브라우저의 보안 매커니즘이 동일 출처 정책 (Same Origin Policy, SOP) 이다.
SOP에서 말하는 "동일 출처"는 오리진을 뜻한다. 이 오리진은 프로토콜, 포트, 호스트로 구성되며, 구성 요소가 모두 일치해야 동일한 오리진이라고 한다.
동일 출처가 아닌 Cross Origin에서 불러온 데이터는 읽으려고 할 때 오류가 발생한다.
crossNewWindow = window.open('https://theori.io');
console.log(crossNewWindow.location.href);
결과: Origin 오류 발생
아래와 같이 읽는 것 외에 데이터를 쓰는 것은 문제없이 동작한다.
crossNewWindow = window.open('https://theori.io');
crossNewWindow.location.href = "https://dreamhack.io";
추가적으로, 이미지나 자바스크립트, CSS 등의 리소스를 불러오는 <img>, <style>, <script> 태그는 SOP의 영향을 받지않는다.
웹 서비스에서는 SOP를 완화하여 다른 출처의 데이터를 처리 해야 하는 경우도 있다. 특정 포털사이트가 카페, 블로그, 메일 서비스를 운영할때 각 서비스의 Host가 다르기 때문에 메인페이지에서 메일의 개수를 출력할 수 없는 현상이 나타난다.
위와 같은 상황에서 자원을 공유하기 위해 교차 출처 리소스 공유 (Cross Origin Resource Sharing, CORS) 를 사용한다. 이는 CORS와 관련된 HTTP 헤더를 추가하여 전송하는 방법을 사용한다.
웹 애플리케이션이 다른 출처 리소스에 접근할 때는 HTTP header에 요청을 보내는데, HTTP 프로토콜을 사용하며, 요청 헤더에 Origin 필드에 요청을 보내는 출처를 담아서 보낸다.
이후 서버가 이 요청에 대한 응답을 할 때 응답 헤더의 Access-Control-Allow-Origin 이라는 값에 "리소스 접근이 허용된 출처"를 내려주고, 응답을 받는 브라우저는 Origin 과 Access-Control-Allow-Origin 을 비교하고 유효한 응답인지 확인한다.
일반적으로 가장 많이 마주치는 시나리오, 브라우저는 요청을 한 번에 보내지 않고 예비 요청과 본 요청으로 나누어서 서버로 전송한다. 이 예비 요청을 Preflight라고 하며, 예비 요청에는 OPTIONS 메소드가 사용된다.
브라우저의 예비 요청에 대한 응답으로 서버는 현재 어떤 것을 허용하고, 어떤 것을 금지하고 있는지에 대한 정보를 응답 헤더에 담아서 보내준다.
이후 브라우저는 예비 요청과 서버의 응답에 담긴 정책을 비교하여 안전하다고 판단되면, 같은 엔드포인트로 다시 본 요청을 보내게 된다. 그리고 서버가 본 요청에 응답을 하면 브라우저는 최종적으로 응답 데이터를 자바스크립트에 넘겨준다.
단순 요청은 예비 요청을 보내지 않고 서버에 본 요청을 보낸 후, 서버가 응답 헤더에 Access-Control-Allow-Origin 과 같은 값을 보내면 그 때 브라우저가 CORS 정책 위반 여부를 검사하는 방식이다.
예비 요청을 생략하는 단순 요청은 특정 조건을 만족해야한다.
🔸요청 메소드는 GET, HEAD, POST 중 하나여야 한다.
🔸Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width 를 제외한 헤더를 사용하면 안된다.
🔸만약 Content-Type 를 사용하는 경우 application/x-www-form-urlencoded, multipart/form-data, text/plain 만 허용된다.
인증된 요청을 사용하는 방법으로 CORS 기본적인 방식보다 다른 출처 간 통신에서 조금 더 보안을 강화하고 싶을 때 사용하는 방법.
XMLHttpRequest 객체나 fetch API는 별도의 옵션없이 브라우저의 쿠키 정보나 인증과 관련된 헤더를 요청에 담지 않는다. 이때 요청에 인증 관련 정보를 담을 수 있게 해주는 옵션이 바로 credentials 옵션이다.
이 옵션에는 3가지의 값을 사용할 수 있다.
| 옵션 값 | 설명 |
|---|---|
| same-origin(default) | 같은 출처 간 요청에만 인증 정보를 담을 수 있다 |
| include | 모든 요청에 인증 정보를 담을 수 있다 |
| omit | 모든 요청에 인증 정보를 담지 않는다 |
same-origin 이나 include 와 같은 옵션을 사용하여 리소스 인증 정보가 포함되면, 브라우저는 Cross-Origin 리소스를 요청할 때 단순히 Access-Control-Allow-Origin 만 확인하는 것이 아니라 조금 더 빡빡한 검사 조건을 추가하게 된다.