말 그대로 웹에서 같은 출처의 리소스만 공유가 가능하도록 하는 보안 방식이다.
그렇다면 여기서 같은 '출처'는 무엇을 의미할까?
이 세가지가 모두 같아야 same origin이라고 인정한다.
https://www.google.com:443 과 같은 url에서
프로토콜은 https, 호스트는 www.google.com, 포트는 443이다.
동일 출처 정책은 웹 애플리케이션이 다른 출처에서 가져온 리소스와 상호작용하는 것을 방지함으로써 잠재적으로 해로울 수 있는 문서를 분리한다. 따라서 공격 받을 수 있는 경로가 줄어들고, 해킹 등의 위협에서 좀 더 안전해질 수 있다.
하지만 다른 출처의 리소스에 접근할 수 없다면 웹 애플리케이션에 많은 제약이 생길 것이다. 예를 들어 카카오 로그인 기능이 있는 웹 사이트를 만들려고 할 때, api를 사용해서 다른 출처의 리소스를 받아야한다.
이런 문제를 해결하기 위해서 '교차 출처 리소스 공유', 즉 CORS를 이용한다.
CORS는 SOP와 반대되는 개념이다. CORS는 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 리소스를 받아올 수 있는 권한을 부여해서 브라우저에게 알려주는 체제이다.
어떤 방식으로? 추가 HTTP 헤더를 사용한다.
웹 애플리케이션은 사용하는 리소스가 자신의 출처와 다를 때 CORS 요청을 실행한다.
좀 더 자세히 알아보자.
우선 domian-a.com 에서 domain-a.com의 리소스를 가져오는 것은 항상 허용된다.
하지만 domain-a.com에서 domain-b.com의 리소스를 가져오려면 domain-b.com의 서버에서 올바른 CORS 헤더를 포함한 응답을 반환해야만 리소스를 무사히 받아올 수 있다.
(✏️ 참고로 CORS 실패는 오류의 원인이 될 수 있지만, 보안상의 이유로 javascript에서는 오류의 상세 접근할 수 없다. 정확히 알아내려면 브라우저의 콘솔을 봐야한다.)
CORS가 동작하는 방식에는 두가지가 있다. 단순 요청(simple requests)와 프리플라이트 요청(preflight requests)이다.
특정 조건을 충족하면 HTTP 요청에서 preflight 요청이 필요없다.
위 조건을 '모두' 충족하면 preflight request를 생략하고 요청을 보낸다.
1. 클라이언트에서 요청 헤더에 자신의 Origin을 포함한 요청을 서버로 보낸다.
2. 서버가 유효한 CORS 요청인지 확인하고 응답 헤더에 Access-Control-Allow-Origin을 추가해 응답한다.
사진처럼 Access-Control-Allow-Origin: * 으로 설정한 경우에는 모든 출처에서 자신의 리소스에 접근 가능하도록 허용한다는 뜻이고, 특정 출처를 지정해줄 수도 있다.
'pre'라는 접두어에서 알 수 있듯이 프리플라이트 요청은 미리 무언가 하는 방식이다.
실제 요청을 보내기 전, 브라우저가 서버에서 OPTIONS 메서드로 사전 요청을 보내 해당 출처의 리소스에 접근 권한이 있는지부터 확인한다.
OPTIONS 메서드로 자신의 Origin을 포함한 요청을 보낸다.
(Content-Type이 application/xml이기 때문에 preflighted 처리가 된다.)
Access-Control-Allow-Origin에 request를 보낸 Origin이 포함되어 돌아온다.
POST 메서드로 실제 요청을 보낸다.
접근 권한이 있는 경우
→ 응답 헤더의 Access-Control-Allow-Origin으로 요청을 보낸 출처가 Response로 돌아오고 → 실제로 요청을 보낸다.
접근 권한이 없는 경우
→ 응답 헤더에 Access-Control-Allow-Origin이 없고 CORS 에러가 나서 실제 요청이 전달되지 않는다.
요청 헤더에 인증 정보를 담아야할 때가 있다.
이 때 같은 Origin에서 http 통신을 한다면 쿠키가 알아서 request 헤더에 들어가지만,
Origin이 다를 경우에는 별도의 설정을 하지 않으면 쿠키를 보낼 수 없다.
따라서 클라이언트와 서버 모두 CORS 설정을 해주어야 한다.
1. 프론트(클라이언트)
//fetch API
fetch(url, {
credentials: 'include'
})
//XMLHttpRequest
const xhr = new XHMLHttpRequest()
xhr.withCredentials = true;
2. 서버
응답 헤더에 Access-Control-Allow-Credentials:true
로 설정해주어야 한다. 이 때 true 대신 와일드카드(*)로 설정하면 에러가 발생한다.
const http = require('http');
const server = http.createServer((request, response) => {
//모든 도메인에 대해 설정
response.setHeader("Access-Control-Allow-Origin", "*");
//특정 도메인에 대해
response.setHeader("Access-Control-Allow-Origin", "https://www.google.com/");
//인증 정보를 포함한 요청일 경우
response.setHeader("Access-Control-Allow-Credentials", "true");
})
CORS 미들웨어를 사용한다.
const cors = require("cors");
const app = express();
//모든 도메인에 대해
app.use(cors());
//특정 도메인에 대해
const options = {
origin: "https://www.google.com/",
credentials: true,
optionsSuccessStatus: 200
};
app.use(cors(options));
//특정 요청에 대해
app.get("/example:id", cors(), function(req, res, next) {
res.json({msg: "example" });
});
참고
https://developer.mozilla.org/ko/docs/Web/HTTP/CORS
https://yoo11052.tistory.com/139