CORS

마이크로서비스 아키텍처(MSA) 패턴의 보안 측면에서 CORS(Cross-Origin Resource Sharing)는 매우 중요한 역할을 한다.

CORS는 웹 브라우저에서 구현된 보안 메커니즘으로, 다른 출처(도메인, 프로토콜, 포트)의 리소스에 접근할 수 있도록 허용하는 체계이다. 이는 동일 출처 정책(Same-Origin Policy)의 제한을 안전하게 우회할 수 있게 해준다.

CORS는 MSA 환경에서 안전하고 유연한 리소스 공유를 가능하게 하는 핵심 메커니즘으로 올바르게 구현된 CORS는 마이크로서비스 간의 안전한 통신을 보장하며, 전체 시스템의 보안을 강화한다.

CORS의 작동 원리

  1. 브라우저가 다른 출처로 HTTP 요청을 보낼 때 Origin 헤더를 추가한다.
  2. 서버는 Access-Control-Allow-Origin 헤더로 응답하여 해당 출처의 접근을 허용할지 결정한다.
  3. 브라우저는 이 헤더를 확인하여 요청을 허용하거나 차단한다.

Origin의 정의
Origin은 다음 세 가지 요소로 구성된다:

  • 프로토콜: 예를 들어, http 또는 https
  • 호스트명: 예를 들어, example.com
  • 포트 번호: 예를 들어, 80 또는 443
    두 URL의 프로토콜, 호스트명, 포트 번호가 모두 동일하면 동일 출처로 간주되며, 하나라도 다르면 교차 출처로 간주된다.

브라우저의 CORS 요청 유형

브라우저는 요청의 성격에 따라 다음과 같은 방식으로 CORS 요청을 처리한다:

  • 단순 요청(Simple Request): GET, HEAD, POST 메서드 중 하나를 사용하고, 특정 조건을 만족하는 요청이다. 이러한 요청은 사전 검증 없이 바로 서버로 전송된다.
  • 프리플라이트 요청(Preflight Request): 클라이언트가 실제 요청을 보내기 전에 OPTIONS 메서드를 사용하여 서버의 CORS 정책을 확인하는 요청이다. 이는 안전하지 않은 메서드나 헤더를 사용할 때 발생한다.
  • 자격 증명 포함 요청(Credentialed Request): 쿠키나 인증 헤더와 같은 자격 증명을 포함한 요청이다. 서버는 Access-Control-Allow-Credentials 헤더를 통해 이러한 요청을 허용할지 결정한다.

CORS의 중요성

  1. 보안 강화: 무단 접근과 잠재적 공격으로부터 웹 애플리케이션을 보호한다.
  2. 유연성: 프론트엔드와 백엔드의 분리, 마이크로서비스 아키텍처 구현을 가능하게 한다.
  3. API 통합: 외부 API와의 안전한 통신을 가능하게 한다.

MSA에서의 CORS 활용

MSA 환경에서 CORS는 더욱 복잡한 역할을 수행한다:

  1. API 게이트웨이: 중앙 진입점으로 작용하여 모든 하위 서비스에 대한 CORS 정책을 관리한다.
  2. 일관된 보안 정책: 게이트웨이에서 CORS를 관리함으로써 모든 마이크로서비스에 대해 일관된 보안 정책을 적용할 수 있다.
  3. 세분화된 접근 제어: 공개 서비스와 민감한 서비스에 대해 다른 CORS 요구사항을 적용할 수 있다.

CORS 구현 시 주의사항

  1. 구체적인 출처 지정: 가능한 한 구체적으로 허용할 출처를 지정한다.
  2. 와일드카드 사용 주의: ‘*‘를 사용한 모든 출처 허용은 보안 위험을 초래할 수 있다.
  3. 자격 증명 처리: Access-Control-Allow-Credentials 헤더를 신중하게 사용한다.
  4. 프리플라이트 요청 처리: OPTIONS 메서드에 대한 적절한 응답을 구현한다.

보안 모범 사례

  1. 정기적인 감사: CORS 정책을 정기적으로 검토하고 업데이트한다.
  2. 환경 변수 사용: 개발 및 프로덕션 설정을 쉽게 전환할 수 있도록 환경 변수를 사용한다.
  3. 철저한 테스트: 다양한 시나리오에서 CORS 구성을 테스트한다.
  4. 최신 보안 권장 사항 준수: CORS 구현을 최신 보안 권장 사항에 맞춰 유지한다.

구현 예시

Node.js/Express를 사용한 CORS 구현 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// CORS 미들웨어 구현
const cors = (options = {}) => {
    return (req, res, next) => {
        // 1. Origin 검증
        const requestOrigin = req.headers.origin;
        const allowedOrigins = options.origins || ['http://localhost:3000'];
        
        if (allowedOrigins.includes(requestOrigin)) {
            // 허용된 출처인 경우
            res.setHeader('Access-Control-Allow-Origin', requestOrigin);
        } else {
            // 허용되지 않은 출처인 경우
            return res.status(403).json({
                error: '허용되지 않은 출처입니다.'
            });
        }
        
        // 2. 메서드 검증
        const allowedMethods = options.methods || ['GET', 'POST', 'PUT', 'DELETE'];
        res.setHeader('Access-Control-Allow-Methods', allowedMethods.join(', '));
        
        // 3. 헤더 검증
        const allowedHeaders = options.headers || ['Content-Type', 'Authorization'];
        res.setHeader('Access-Control-Allow-Headers', allowedHeaders.join(', '));
        
        // 4. 자격 증명 (Credentials) 설정
        if (options.credentials) {
            res.setHeader('Access-Control-Allow-Credentials', 'true');
        }
        
        // 5. Preflight 요청 처리
        if (req.method === 'OPTIONS') {
            return res.status(204).end();
        }
        
        next();
    };
}

참고 및 출처