[네트워크] CORS와 credentials

가르송·2023년 4월 6일
1
post-thumbnail

이번 주에는 http 모듈을 불러와 서버를 구현하고, 이를 express를 이용하여 리팩토링하는 시간을 가졌다. 이 과정에서 만난 CORS가 무엇인지, 실제 프로젝트에서 어떻게 다루면 좋은지 작성해 보고자 한다.

CORS란?

CORS(Cross-Origin Resource Sharing, 교차 출처 리소스 공유)는 웹 애플리케이션이 다른 출처의 리소스에 접근해야 할 때, 애플리케이션이 접근 권한을 부여받도록 브라우저에게 알려주는 체제이다.

  • 기본적으로 브라우저는 SOP 때문에 다른 출처와 리소스를 공유할 수 없다. 그러나 웹 애플리케이션의 기능이 다양해지면서 다른 출처의 리소스를 향한 니즈가 증가했다. 이러한 문제를 해결하기 위해 등장한 것이 바로 CORS이다. CORS를 사용하면 막혀있는 리소스에의 접근 권한을 얻을 수 있다.

SOP(Same-Origin Policy, 동일 출처 정책
같은 출처(Origin: 프로토콜, 호스트, 포트의 조합)의 리소스만 공유가 가능하다는 정책이다. '같은 출처'이므로 프로토콜이나 호스트, 포트 중 어느 하나라도 다르다면 SOP를 위반하는 것이다. 이 정책은 다른 사이트와의 리소스 공유를 제한하여 중요한 정보가 타 사이트의 코드에 의해 새어나가는 것을 방지한다. 이러한 보안상의 이점 때문에 모든 브라우저는 기본적으로 SOP 정책을 사용한다.


동작 방식

CORS는 크게 세 가지 방식으로 동작한다.

1. Preflight Request(프리플라이트 요청)

글자 그대로 실제로 요청을 보내기 전에 사전 요청(Preflight Request)을 보내보는 것이다. 이 요청은 OPTIONS 메서드로 보내지며 해당 출처 리소스에 접근 권한이 있는지 확인하는 역할을 한다.

  1. 클라이언트가 [실제 요청]을 보내면 브라우저는 우선 서버에 [Preflight Request]를 보낸다. 이때 요청은 다음과 같은 정보를 포함하고 있다.
// 실제 요청 시 어떤 메서드와 헤더가 사용될 것인지 미리 서버에게 알려준다
'Access-Control-Request-Method': 'POST',
'Access-Control-Request-Headers': 'Content-Type'
  1. 서버는 [Preflight Request]의 정보를 검토하여 실제 요청에 응할지 응하지 않을지 그 여부를 결정한다.
    2-1. 요청에 응하지 않을 경우 CORS 에러가 발생한다.
    2-2. 요청에 응할 경우 다음과 같은 정보를 담아 응답한다. 아래 예시는 와일드카드로 모든 도메인을 허용하고 있지만, 특정한 Origin만 용납하는 경우가 많고 그것이 더 안전한 방식이다.
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Accept',
  'Access-Control-Max-Age': 10
  1. 브라우저는 [Preflight Request]의 응답으로 돌아온 헤더의 Access-Control-Allow-Origin을 확인한다. 접근 권한이 있음을 확인한 브라우저는 [실제 요청]을 보낸다.
  2. 서버는 실제 요청을 수행 후 [Response]를 보내고, 브라우저는 이를 클라이언트에 전달한다.

2. Simple 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 (인증정보를 포함한 요청)

다른 출처 사이의 통신에서 보안을 강화하고 싶을 때 사용하는 방법으로, 헤더에 인증 정보를 담아 요청을 보낸다.

  • 기본적으로 출처가 다른 경우에는 쿠키나 인증 관련 헤더(헤더에 Authorization 항목이 있는 요청)를 보낼 수 없다. 민감한 정보이기 때문이다. 때문에 그것이 가능하도록 하려면 프론트와 서버 양측 모두 CORS를 설정해야 한다.
  • 프론트엔드의 요청과 서버의 응답 모두 CORS 설정이 필요하다.

프론트엔드에서 할 일

1. fetch API의 경우

  • fetch API에서는 credentials 옵션을 사용하는데, 세 가지 값 중 하나를 가질 수 있다.
    • same-origin(기본값) : 같은 Origin 사이에만 인증 정보를 담는다.
    • include : 모든 요청에 인증 정보를 담는다.
    • omit : 모든 요청에 인증 정보를 담지 않는다.
fetch("https://example.com:1000/projects", {
  credentials: "include",
});

2. axios의 경우

  • axios에서는 withCredentials 옵션을 사용하며, 불리언 값을 가진다.
    • false(기본값)
    • true : Credentialed Request를 위해서는 true로 설정해야 한다.
axios.get("https://example.com/items", {
  withCredentials: true,
});

서버에서 할 일

  1. 응답 헤더에 Access-Control-Allow-Credentials : true 를 넣어준다.
  2. 응답 헤더의 Access-Control-Allow-Origin 을 정확하게 설정한다. 즉, 와일드카드(*)를 사용하지 않는다.
  3. Access-Control-Allow-MethodsAccess-Control-Allow-Headers 의 값을 지정해야 할 경우 와일드카드(*)를 사용하지 않는다.

참조

profile
개발도 운동도 뜻대로 되지 않을 때에는? 산책을 합니다.

0개의 댓글