본문 바로가기
코드스테이츠

2/6 일일정리 CORS, node server 실습

by 강물둘기 2023. 2. 6.

SOP(Same-Origin Policy)

동일 출처 정책. 즉, 같은 출처의 리소스만 공유가 가능하다는 정책이다. 다른 여러 출처로부터 상호작용을 제한한다.

위의 URI에서 프로토콜, 호스트, 포트가 모두 같아야 동일한 출처로 판단한다.

 

동일 출처 정책을 사용하는 이유는 잠재적인 공격 경로를 줄이기 위함이다.

SOP는 다른 사이트와의 리소스 공유를 제한하기 때문에 로그인 정보같은 개인정보가 타 사이트의 코드에 의해서 새어나가는 것을 방지할 수 있다. 이러한 보안상 이점 때문에 SOP은 모든 브라우저에서 기본적으로 사용하고 있는 정책이다.

 

CORS(Cross-Origin Resource Sharing)

기본적으로 SOP를 사용하지만, 우리는 실제로 개발을 할 때 다른 많은 출처로부터 리소스를 받아와야 한다. 

이러한 상황에서 필요한것이 CORS(Cross-Origin Resource Sharing)이다.

교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다. -MDN

브라우저는 기본적으로 SOP를 사용하기 때문에 다른 출처의 리소스 공유를 막지만, CORS를 사용하여 접근 권한을 얻을 수 있다.

 

CORS 동작 방식

1. Preflight Request

 실제 요청을 보내기 전에 OPTION 메서드로 다른 도메인의 리소스로 HTTP 요청을 보내 실제 요청이 전송하기에 안전한지 확인한다.

출처 : https://evan-moon.github.io/2020/05/21/about-cors/

브라우저는 서버에 실제 요청을 보내기 전에 preflight request를 보내고, 응답 헤더의 Access-Control-Allow-Origin으로 요청을 보낸 출처가 돌아오면 실제 요청을 보낸다.

접근권한이 없으면 브라우저에서 CORS 에러를 띄우고 실제 요청은 전송되지 않는다.

 

- 실제 요청을 처음부터 통째로 보내는 것 보다 미리 권한을 확인하고 보내는 것이 리소스적인 측면에서 효율적이다.

- CORS에 대비가 되어있지 않은 서버를 보호할 수 있다. CORS 이전에 만들어진 서버들은 SOP 요청만 들어오는 상황을 고려하고 만들어졌기 때문에 다른 출처에서 들어오는 요청에 대한 대비가 되어있지 않을 수 있다.

 

2. 단순 요청(Simple Request)

preflight request를 생략하고 바로 실제 요청을 보내는 것을 말한다. 특정 조건을 만족하는 경우에만 예비 요청을 생략할 수 있다. 조건이 까다롭기 때문에 사용하기 힘들다. 조건은 다음과 같다.

- GET, HEAD, POST 요청 중 하나여야 합니다.
- 자동으로 설정되는 헤더 외에, Accept, Accept-Language, Content-Language, Content-Type 헤더의 값만 수동으로 설정할 수 있습니다.
- Content-Type 헤더에는 application/x-www-form-urlencoded, multipart/form-data, text/plain 값만 허용됩니다.

 

3. 인증 정보를 포함한 요청(Credentialed Request)

요청 헤더에 인증 정보를 담아 보내는 요청이다. 다른 출처간 통신에서 좀 더 보안을 강화하고 싶을 때 사용한다.

이 경우는 클라이언트와 서버 양측 모두 CORS 설정이 필요하다.

클라이언트에서는 요청 헤더에 withCredentials : true 를 넣어줘야 한다.

서버에서는 응답 헤더에 Access-Control-Allow-Credentials : true 를 넣어줘야 한다.

* 서버에서 Access-Control-Allow-Origin 을 설정할 때, 와일드카드(*)로 설정하면 에러가 발생한다.

 

 

Mini Node server 실습

페어와 함께 mini node server 실습을 진행하였다.

요청란에 단어를 작성하고  버튼을 클릭하면 대문자나 소문자로 바뀐 응답을 받는 서버를 http 모듈의 createServer로 구축해보았다.

 

응답을 받으려면 CORS를 준수해야 하기 때문에 다음과 같이 미리 작성된 헤더를 쓴다.

 

응답 메서드, 응답 url에 따라 다른 응답을 해야하기 때문에 조건문을 사용하여 응답을 작성해야 한다.

이번 실습에서는 preflight 요청과 POST 요청만 사용하기 때문에 요청메서드가 OPTIONS인 것과 POST 인 것 두개로 구분하고, POST 요청은 클릭하는 버튼에 따라 url이 /upper 인 것과 /lower 인 것을 구분한다.

나머지 요청은 전부 400 상태코드를 응답해준다.

 

요청 body에 우리가 입력한 단어가 담겨서 서버에 전달되는데 서버측에서 받은 단어를 대문자나 소문자로 바꾸어서 다시 응답으로 되돌려줘야 한다.

핸들러에 전달된 request 객체는 ReadableStream 인터페이스를 구현하고 있습니다.
이 스트림에 이벤트 리스너를 등록하거나 다른 스트림에 파이프로 연결할 수 있습니다.
스트림의 'data'와 'end' 이벤트에 이벤트 리스너를 등록해서 데이터를 받을 수 있습니다.

node 공식 문서에 위와 같이 적혀있는데 명확한 작동방식은 좀 더 공부를 해봐야 할 것 같다.

 

어쨋든 request 객체에서 body를 가져오기 위해서 .on 메서드와 Buffer를 이용한다.

let body = [];
request.on('data', (chunk) => {
  body.push(chunk);
}).on('end', () => {
  body = Buffer.concat(body).toString();
  // 여기서 `body`에 전체 요청 바디가 문자열로 담겨있습니다.
});

 

위의 코드로 받아온 body를 on('data') 핸들러에서 읽고, on('end') 핸들러에서 응답할 파일내용을 설정해줘야한다.

따라서 위에서 작성한 조건문을 on('end') 핸들러 내부에 넣고 응답 코드를 완성시키면 된다.

 

응답을 작성할 때 헤더를 만들어주지 않으면 CORS 에러가 나기 때문에 꼭 헤더를 작성해야 한다.

 

 

댓글