kimyu0218
  • [etc] CORS 이해하기 (Feat. *에서 탈출하기)
    2024년 01월 21일 03시 25분 24초에 업로드 된 글입니다.
    작성자: @kimyu0218

    CORS 에러에 부딪히면 항상 origin에다 * 넣어서 해결했는데 드디어 CORS에 대해 공부해보기로 결심했다.
     

    CORS

    CORS란?

    CORS는 Cross-Origin* Resource Sharing의 약자다. Resource Sharing이 자원 공유인 건 알겠는데 Cross Origin은 뭘까? 크로스 오리진은 서로 다른 출처를 의미한다. 즉, CORS는 서로 다른 출처 간 자원 공유다.

    *origin : 자원에 접근하는 출처 (프로토콜 + 호스트 + 포트)

    SOP: Same-Origin Policy

    😈 origin에다 *를 넣으면 안되는 이유
    프론트엔드와 백엔드 A는 백엔드 B와 다른 출처에 있기 때문에 백엔드 B에 접근할 수 없다.

    CORS 요청은 보안상의 이유로 Same-Origin Policy에 의해 제한된다. Same-Origin Policy는 다른 출처에서의 접근을 막아 자원을 보호한다. 즉, CSRF 공격을 막을 수 있는 보안 정책이다.
    어떻게 CSRF 공격으로부터 방어할 수 있는지 여기에 아주 잘 정리되어있다. (CSRF 외에도 CORS에 대해 쉽게 설명되어 있으니 다 읽어보는 것 추천한다!!)

    origin을 *로 설정하면 CSRF 공격에 취약하다

    CORS로 Same-Origin Policy 우회하기

    하지만 종종 다른 출처의 자원에 접근해야 하는 상황이 생긴다. 프론트엔드와 백엔드 주소가 다른 경우, 프론트엔드에서 백엔드의 자원을 조회하기 위해서는 SOP를 우회해야 한다.

    어떻게 SOP를 우회할 수 있을까? 결론부터 말하자면 백엔드에서 `Access-Control-Allow-Origin` 헤더에 접근을 허용할 출처를 설정해줘야 한다.
     

    CORS의 동작과정

    서버에서 CORS 설정을 해줘야 하는 이유를 CORS 동작과정과 함께 알아보자.

    클라이언트에서 HTTP 요청을 보낼 때, 헤더에 `Origin`을 담아 보낸다.

    서버는 헤더에 `Access-Control-Origin`을 담아 요청에 대한 응답을 전송한다. (그림을 잘 보면 아직 클라이언트에게 완전히 전달된 것이 아니다!)

    브라우저에서 응답을 받으면, 요청의 `Origin`과 응답의 `Access-Control-Allow-Origin`을 비교하여 차단 여부를 결정한다. 두 주소가 서로 일치하지 않는다면 CORS 에러가 발생한다.
     

    nest 프레임워크에서 CORS 설정하기

    프레임워크마다 CORS 설정방법이 다르므로 nest 프레임워크만 다룰 것이다.

    const app: INestApplication = await NestFactory.create(AppModule);
    
    // 서버에서 설정하는 CORS 설정은 Access-Control-Allow-*
    app.enableCors({
      // Access-Control-Allow-Origin
      origin: process.env.CORS_ALLOW_DOMAIN,
      // Access-Control-Allow-Methods
      methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
      // Access-Control-Allow-Credentials
      credentials: true,
      // Access-Control-Allow-Headers
      allowedHeaders: ['Authorization', 'Content-type'],
    });

    `origin`에 접근을 허용할 오리진 목록들을 작성하고, `methods`에 허용할 HTTP 메서드를 정의한다. `credentials`은 자격증명을 의미하는데, true로 설정하여 쿠키, HTTP 인증정보 등을 받을 수 있도록 설정했다.
    `allowedHeader`는 말 그대로 허용할 HTTP 헤더로, 클라이언트가 요청에 담아 보내는 헤더 정보를 제한한다. 나는 `Authorization`과 `Content-Type`을 받을 수 있도록 설정했다.
     


    참고자료

    댓글