CORS에 대하여

Heechan Kang·2024년 1월 8일
post-thumbnail

CORS(Cross Origin Resource Sharing; 교차 출처 리소스 공유)란?

  • 다른 도메인 간의 리소스 공유 시, 각 도메인의 리소스에 대한 접근 권한을 조정하기 위해 브라우저에서 강제하는 프로토콜입니다.
  • 예를 들어, A-domain.com에서 B-domain.com의 리소스에 함부러 접근하여 악용하는 등의 문제점을 막기 위한 보안 조치입니다.
  • 하지만 최근들어, 과도한 제약이라는 의견이 대두되고 있기도 하고, 종종 상상하지 못한 곳에서 문제를 발생시켜 개발자들의 복병처럼 느껴지기도 합니다.

📝 주요 개념

✅ 출처(Origin)

  • CORS의 가장 기본이 되는 개념입니다.

  • 출처의 예시: https://naver.com:443

  • 스키마, 호스트명, 포트로 구성됩니다.

    • 스키마: http, https 등
    • 호스트명: 도메인 주소, IP 주소 등
    • 포트: 0 ~ 65535
  • 위 세 가지 구성요소 중 하나만 다르더라도 다른 출처로 간주됩니다.

    ✅ 단순 요청(Simple Requests)

  • 특정 요건을 충족한, 안전한 요청입니다.

    • 특정 요건
      • 요청 메소드가 GET, HEAD, POST 이어야 합니다.
      • 아래와 같은, 표준 헤더만을 사용해야 합니다
      • Content-Type이 application/x-www-form-urlencoded, multipart/form-data, text/plain 중 하나이어야 합니다.
  • 즉, 흔히 사용하는 POST, Content-Type이 application/json인 경우도 단순요청이 될 수 없습니다.

    ✅ 사전 요청(Preflight Requests)

  • 단순 요청이 아닌 경우, 해당 요청이 안전한지 판단하기 위해 본 요청 전 사전요청을 보냅니다.

  • 사전 요청은 OPTIONS 메소드를 사용해 요청되며, 아래의 사항들을 확인합니다.

    • 어떤 출처(Origin)을 허용하는가?: Host
    • 어떤 메소드를 허용하는가?: Access-Control-Request-Method
    • 어떤 헤더를 허용하는가?: Access-Control-Request-Headers
  • 요청받은 서버는, 해당 요청에 대해 아래와 같이 답신합니다.

    • 해당 출처에서만 사용을 허용한다: Access-Control-Allow-Origin
    • 이러한 메소드만 허용한다: Access-Control-Allow-Methods
    • 이러한 헤더만 허용한다: Access-Control-Allow-Headers
    • 해당 시간동안 같은 조건을 유지하겠다(캐시): Allow-Control-Max-Age

📝 CORS의 작동방식

1. 출처 확인

  • 웹 브라우저에서 요청을 보내는 경우, 브라우저는 자동으로 요청 헤더에 'Origin' 헤더를 추가합니다.

  • 이는 위에서 설명한 것 과 같이, 스키마, 도메인, 포트번호를 포함합니다.

2-1. 단순요청인 경우 - 종료

  • 단순 요청의 경우, 별도의 사전과정 없이 서버에 요청을 전송합니다.

  • 수신한 서버에서는 Access-Control-Allow-Origin 헤더를 통해 사용 가능한 출처를 알립니다

2-2. 사전 요청(단순요청이 아닌 경우. 3으로 계속)

  • 브라우저는 사전요청을 통해 실제 요청이 안전한 요청인지부터 확인합니다.

  • OPTIONS 메소드를 사용하며, Origin, Method, Header의 사용가능여부를 확인합니다.

    • 위 사진은 DELETE 요청에서 Content-Type: application/json 헤더를 실었을 경우의 사전 요청(preflight 요청) 헤더입니다.
    • 이 경우, 세 가지 조건에 의해 사전요청이 보내집니다.
      1. DELETE 메서드를 사용했다.
      2. Content-Type: application/json을 사용했다.
      3. X-Test라는 커스텀 헤더를 사용했다.

3. 실제 요청

  • 사전요청이 성공하면, 브라우저는 서버에서 서용하는 Origin, Header, Method를 알게됩니다.
  • 이를 바탕으로 실제 요청이 적절한지 확인하고, 적절하지 못한 경우 CORS 에러를 표시합니다.
  • 적절한 경우, 실제 요청을 서버에 전달하고 응답을 받습니다.

4. 응답 처리 - 종료

  • 마지막으로, 실제 요청 결과의 Access-Control-Allow-Origin 헤더를 확인한 후, 현재의 Origin에서 사용 가능하다면(전체 허용 - * 이거나 포함된 경우)요청은 성공적으로 처리되고, 자원을 사용 할 수 있게 됩니다.

🖥️ 간단한 예제 코드

express 앱에서, 위에서 다루었던 요청을 브라우저에서 사용 가능하게 하려면 아래와 같은 설정이 필요합니다.

app.options('/json-cors', (req, res) => {
  res.set({
    'Access-Control-Allow-Origin': 'http://localhost:3000',
    'Access-Control-Allow-Methods': 'DELETE, OPTIONS',
    'Access-Control-Allow-Headers': 'Content-Type, X-Test',
  });
  res.sendStatus(204);
});

app.delete('/json-cors', (req, res) => {
  ...
  res.set({
    'Access-Control-Allow-Origin': 'http://localhost:3000',
  })
...
});

CORS 테스트 프로젝트 - 링크

Express와 ejs 템플릿을 사용한 간단한 CORS 테스트 프로젝트입니다.

👍 결론

결과적으로 CORS는 웹 브라우저에서 보안성을 지키기 위해 적절히 사용해야할 하나의 도구입니다.
따라서 우리 개발자들은, 이를 적절히 이해하고 활용할 필요가 있고, 이를 통해 보안성을 지키면서도, 자원 공유간에 불필요한 충돌을 막고, 리소스 낭비를 최소화 할 수 있습니다.

profile
안녕하세요!

0개의 댓글