Origin
•
Origin은 URL의 프로토콜, 호스트, 포트 이 3가지로 정의된다.
•
즉 어떤 URL이 같은 Origin(출처)인지 판단하려면 URL의 프로토콜, 도메인, 포트 번호가 모두 같은지 확인하면 된다.
SOP (Same-Origin Policy): 동일 출처 정책
•
동일한 출처 사이에서만 자원을 공유할 수 있다는 규칙을 가진 보안 정책이다.
◦
이 정책은 사용자를 CSRF 등의 공격으로부터 보호하기 위한 정책으로 시행되었다.
•
옛 웹 어플리케이션은 프론트엔드와 백엔드를 분리하지 않는 경우가 많았다. 따라서 하나의 어플리케이션으로 취급되었는데, 서버가 요청에 대한 결과를 만들어 클라이언트에게 보내주었다.
•
모든 처리가 같은 출처에서 일어나므로 애초에 다른 출처로 요청을 보낼 필요가 없었다.
◦
그 당시엔 다른 출처로 처리를 요청하는 것을 악의적인 행위(CSRF, XSS 등)으로 간주하는 것이 자연스러웠다.
•
하지만 웹 기술이 발전함에 따라 자연스럽게 다른 출처로 요청을 하고 응답을 받아오는 경우가 많아졌다.
CORS (Cross-Origin Resrouce Sharing): 교차 출처 리소스 공유
•
어플리케이션의 프론트 어플리케이션이 제 3자가 제공하는 API를 직접 호출해야 하는 등 점점 요구되는 기능이 늘어났다.
◦
하지만 SOP 정책 때문에 일반적인 방법으로는 사용이 불가능했고 외부 API를 호출하기 위한 근본적인 해결 방법이 필요했다.
•
이런 배경으로 CORS가 등장했다. 리소스 호출이 허용된 Origin을 서버가 명시해놓으면 출처가 다르더라도 요청과 응답을 주고 받을 수 있도록 한 정책이 CORS다.
•
접근 제어 시나리오
◦
브라우저는 다른 출처로 요청을 보낼 때, 자동으로 요청 헤더에 Origin을 추가해서 보낸다.(단순요청)
▪
요청이 단순 요청이러면 다음 조건들을 만족해야 한다.
•
GET, POST, HEAD HTTP 메소드만 허용한다.
•
Accept, Accept-Language, Content-Language, Content-Type 헤더만 허용한다.
◦
Content-Type은 application/x-www-form-urlencoded, multipart/form-data, text/plain만 허용한다.
◦
이 응답을 받은 서버는 응답 헤더에 Access-Control-Allow-Orgin을 추가해서 보낸다.
▪
이 헤더에는 허가된 출처 정보가 기록되어 있다. 와일드카드(*)를 사용해서 모든 출처를 허용할 수 있다.
◦
브라우저는 요청의 Origin 헤더에 담긴 출처 정보가 응답의 Access-Controll-Allow-Origin 헤더에 담겨 있으면 해당 요청이 요청을 안전하다고 간주하고 응답을 가져온다.
▪
그렇지 않다며 해당 응답을 임의로 파기하고 응답의 내용을 전달하지 않는다.
•
프리플라이트 요청
◦
실제 요청을 보내기 전, 사전 요청을 보내서 해당 자원에 접근이 가능한지 먼저 확인하는 방식을 Preflight 방식이라고 한다.
▪
OPTIONS HTTP 메소드를 통해 요청된다.
◦
프리플라이트 요청은 단순 요청과 마찬가지로 요청에 Origin 헤더를 추가해야 한다.
▪
추가적으로 실제 요청의 메소드를 Access-Control-Request-Method 헤더에 실제 요청의 헤더들을 Access-Control-Request-Headers 헤더에 담아 보내야 한다.
◦
응답 역시 단순 요청과 마찬가지로 Access-Control-Allow-Orgin을 추가해서 보낸다.
▪
추가적으로 서버에서 허용하는 메소드와 헤더가 담긴 Access-Controll-Allow-Method 헤더와 Access-Control-Allow-Headers 헤더, 캐시 기간인 Access-Control-Max-Age를 보내준다.
•
프리플라이트가 필요한 이유는 뭘까?
◦
CORS는 서버가 아닌 브라우저 구현 스펙에 포함된 정책 내용이다.
◦
서버는 CORS 위반 여부와 상관없이 일단 요청이 들어오면 처리를 하고 응답을 보낸다.
▪
그 응답을 받은 브라우저가 응답 헤더를 확인해서 응답의 파기 여부를 결정한다.
◦
GET과 HEAD와 같은 단순 요청은 단순 조회를 수행하므로 상관 없으나 POST, PUT, DELETE의 경우 서버에 사이드 이펙트를 발생시킬 수 있는 메소드들이다.
▪
이들은 응답이 파기되어도 해당 동작의 수행은 이뤄진다.
◦
프리플라이트는 실제 요청이 CORS를 위반하지 않는지를 확인하고 사이드 이펙트로부터 서버를 보호하기 위해 전송한다.
▪
POST같은 경우 조건만 만족한다면 프리플라이트 요청 대신 단순 요청으로 전송될 수 있으므로 서버에서도 이에 대한 처리가 필요하다.
◦
프리플라이트 요청은 브라우저에서 자동으로 수행하므로 프로그래머가 직접 전송할 필요는 없다.
▪
Postman을 사용했을 땐, 정상적으로 수행되나 웹 브라우저에 url을 작성하면 CORS가 발생하는 경우를 봤을 것이다.
▪
이는 Postman의 경우 프리플라이트 요청을 수행하지 않기 때문이다.
•
인증정보를 포함한 요청
◦
쿠키, 토큰과 같이 사용자 식별 정보가 담긴 요청에 대해서는 조금 더 엄격하게 적용한다.
◦
클라이언트는 요청을 보낼 때 credentials 옵션을 별도로 설정해줘야 한다.
▪
Axios, XMLHttpRequest 등 구현체마다 옵션이 다르게 존재하므로 사용 시, 잘 확인하고 사용해야 한다.
◦
서버는 응답할 때 Access-Control-Allow-Credentials 헤더를 true로 설정해줘야 한다.
◦
이때 Access-Control-Allow-Origin은 와일드카드로 설정할 수 없으며 명확한 출처를 명시해주어야 한다.