Search
Duplicate
🥍

SOP, CORS

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-Typeapplication/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 위반 여부와 상관없이 일단 요청이 들어오면 처리를 하고 응답을 보낸다.
그 응답을 받은 브라우저가 응답 헤더를 확인해서 응답의 파기 여부를 결정한다.
GETHEAD와 같은 단순 요청은 단순 조회를 수행하므로 상관 없으나 POST, PUT, DELETE의 경우 서버에 사이드 이펙트를 발생시킬 수 있는 메소드들이다.
이들은 응답이 파기되어도 해당 동작의 수행은 이뤄진다.
프리플라이트는 실제 요청이 CORS를 위반하지 않는지를 확인하고 사이드 이펙트로부터 서버를 보호하기 위해 전송한다.
POST같은 경우 조건만 만족한다면 프리플라이트 요청 대신 단순 요청으로 전송될 수 있으므로 서버에서도 이에 대한 처리가 필요하다.
프리플라이트 요청은 브라우저에서 자동으로 수행하므로 프로그래머가 직접 전송할 필요는 없다.
Postman을 사용했을 땐, 정상적으로 수행되나 웹 브라우저에 url을 작성하면 CORS가 발생하는 경우를 봤을 것이다.
이는 Postman의 경우 프리플라이트 요청을 수행하지 않기 때문이다.
인증정보를 포함한 요청
쿠키, 토큰과 같이 사용자 식별 정보가 담긴 요청에 대해서는 조금 더 엄격하게 적용한다.
클라이언트는 요청을 보낼 때 credentials 옵션을 별도로 설정해줘야 한다.
Axios, XMLHttpRequest 등 구현체마다 옵션이 다르게 존재하므로 사용 시, 잘 확인하고 사용해야 한다.
서버는 응답할 때 Access-Control-Allow-Credentials 헤더를 true로 설정해줘야 한다.
이때 Access-Control-Allow-Origin은 와일드카드로 설정할 수 없으며 명확한 출처를 명시해주어야 한다.