SOP
은 Same-Origin-Policy
의 줄임말로 동일 출처 정책을 뜻합니다.
즉, '같은 출처의 리소스만 공유가 가능하다'라는 정책입니다.
출처(Origin) 는 다음과 같습니다.
https://www.codestates.com:443/course
출처는 프로토콜(https://
) , 호스트(www.codestates.com
) , 포스트(:443
) 의 조합으로 돼있습니다. 이 중 하나라도 다르면 동일한 출처로 보지 않습니다.
https://www.codestates.com
vs http://www.codestates.com
두 URI는 프로토콜 이 다르기 때문에 동일 출처가 아닙니다. (https/ http)
https://urclass.codestates.com
vs https://codestates.com
두 URI는 호스트 가 다르기 때문에 동일 출처가 아닙니다.(urclass.codestates.com / codestates.com)
http://codestates.com:81
vs http://codestates.com
http 프로토콜의 기본 포트는 80입니다. 따라서 http://codestates.com
는 http://codestates.com:80
과 동일합니다. 위 두 URI은 포트 가 다르기 때문에 동일 출처가 아닙니다. (:81 / :80)
https://codestates.com:443
vs https://codestates.com
https 프로토콜의 기본 포트는 443입니다. 따라서 https://codestates.com
는 https://codestates.com:443
과 동일합니다. 두 URI는 프로토콜, 호스트, 포트가 모두 같은 동일 출처 입니다.
이렇게 SOP
동일 출처 정책은 애초에 다른 사이트와의 리소스 공유를 제한하기 때문에 해킹 등의 위협에서 보다 더 안전해질 수 있습니다. 이러한 보안상 이점 때문에 SOP는 모든 브라우저에서 기본적으로 사용하고 있는 정책 입니다.
예를 들어, 네이버 같은 웹 페이지에 남은 로그인 정보가 타 사이트의 코드에 의해서 새어나가는 것을 방지할 수 있습니다.
하지만 만약 다른 출처의 리소스를 사용해야되는 일이 발생한다면 어떻게 할 수 있을까요? 이때 필요한 것이 바로 CORS 입니다.
CORS는 Cross-Origin Resource Sharing 의 줄임말로 교차 출처 리소스 공유를 뜻합니다.
MDN에서의 CORS 정의는 다음과 같습니다.
교차 출처 리소스 공유(Cross-Origin Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다.
즉, 브라우저는 SOP에 의해 기본적으로 다른 출처의 리소스 공유를 막지만, CORS를 사용하면 접근 권한을 얻을 수 있게 됩니다.
위 에러는 CORS 에러입니다. 내용을 풀어쓰면 다음과 같습니다.
다른 출처의 리소스를 가져오려고 했지만 SOP 때문에 접근이 불가능합니다. CORS 설정을 통해 서버의 응답 헤더에 'Access-Control-Allow-Origin'을 작성하면 접근 권한을 얻을 수 있습니다.
실제 요청을 보내기 전, OPTIONS 메서드로 사전 요청을 보내 해당 출처 리소스에 접근 권한이 있는지부터 확인하는 것을 프리플라이트 요청이라고 합니다.
위 이미지의 흐름과 같이 브라우저는 서버에 실제 요청을 보내기 전에 프리플라이트 요청을 보내고, 응답 헤더의 Access-Control-Allow-Origin
으로 요청을 보낸 출처가 돌아오면 실제 요청을 보내게 됩니다.
만약에 요청을 보낸 출처가 접근 권한이 없다면 브라우저에서 CORS 에러를 띄우게 되고, 실제 요청은 전달되지 않습니다.
Q. 프리플라이트 요청은 왜 필요한 것인가요?
단순 요청은 특정 조건이 만족되면 프리플라이트 요청을 생략하고 요청을 보내는 것을 말합니다.
조건은 다음과 같습니다. 하지만 아래 조건들은 모두 만족시키기는 어렵습니다.
GET
, HEAD
, POST
요청 중 하나여야 합니다.Accept
, Accept-Langage
, Content-Language
, Content-Type
헤더의 값만 수동으로 설정할 수 있습니다. Content-Type
헤더에는 application/x-www-form-urlencoded
, multipart/form-data
, text/plain
값만 허용됩니다. 요청 헤더에 인증 정보를 담아 보내는 요청입니다. 출처가 다를 경우에는 별도의 설정을 하지 않으면 쿠키를 보낼 수 없습니다. 민감한 정보이기 때문입니다. 이 경우에는 프론트, 서버 양측 모두 CORS 설정이 필요합니다.
withCredentials: true
를 넣어줘야 합니다.Access-Control-Allow-Credentials : true
를 넣어줘야 합니다. Access-Control-Allow-Origin
을 설정할 때, 모든 출처를 허용한다는 뜻의 와일드카드(*)로 설정하면 에러가 발생합니다. 인증 정보를 다루는 만큼 출철ㄹ 정확하게 설정해줘야 합니다.Node.js로 간단한 HTTP 서버를 만들 경우, 다음과 같이 응답 헤더를 설정해줄 수 있습니다.
const http = require('http');
const server = http.createServer((request, response) => {
// 모든 도메인
response.setHeader("Access-Control-Allow-Origin", "*");
// 특정 도메인
response.setHeader("Access-Control-Allow-Origin", "https://codestates.com");
// 인증 정보를 포함한 요청을 받을 경우
response.setHeader("Access-Control-Allow-Credentials", "true");
})
const cors = require("cors");
const app = express();
//모든 도메인
app.use(cors());
//특정 도메인
const options = {
origin: "https://codestates.com", // 접근 권한을 부여하는 도메인
credentials: true, // 응답 헤더에 Access-Control-Allow-Credentials 추가
optionsSuccessStatus: 200, // 응답 상태 200으로 설정
};
app.use(cors(options));
//특정 요청
app.get("/example/:id", cors(), function (req, res, next) {
res.json({ msg: "example" });
});