[CS] SOP와 CORS + Nginx 프록시 설정 실습

Hyunjun Kim·2025년 7월 20일
0

Computer_Science

목록 보기
17/19

1. SOP(Same-Origin Policy)란?

SOP(Same-Origin Policy)는 웹 브라우저의 보안 메커니즘으로, 한 도메인(Origin)에서 실행되는 스크립트가 다른 도메인의 리소스에 접근하는 것을 제한한다.
Origin은 프로토콜(예: HTTP, HTTPS), 도메인(예: example.com), 포트(예: 80, 443)의 조합으로 정의된다.

예를 들어, https://example.com:443http://example.com:80은 서로 다른 Origin이다.

SOP의 목적은 다음과 같다:

  • 보안: 악성 스크립트가 다른 도메인의 데이터를 무단으로 읽거나 수정하는 것을 방지한다(예: XSS 공격 방지).
  • 데이터 무결성: 사용자의 민감한 데이터(예: 쿠키, 인증 토큰)를 보호한다.

데이터 엔지니어링에서는 SOP로 인해 프론트엔드(예: React 앱)에서 백엔드 API(예: REST API)로의 요청이 차단될 수 있다.

예를 들어, https://frontend.example.com에서 https://api.example.com으로 데이터를 요청하면 SOP에 의해 차단된다.



2. CORS(Cross-Origin Resource Sharing)란?

CORS는 SOP를 완화하여 안전한 크로스-오리진 요청을 허용하는 메커니즘이다.
서버가 특정 HTTP 헤더를 응답에 포함시켜 브라우저가 크로스-오리진 요청을 허용할지 결정한다.
API 호출, 데이터 스트리밍, 클라우드 서비스와의 통신에서 CORS가 필수적이다.

2.1. CORS의 동작 방식

  • 단순 요청(Simple Request): GET, POST, HEAD 메서드와 기본 헤더(예: Accept, Content-Type)만 사용하는 요청. 브라우저는 바로 요청을 보내고, 서버는 Access-Control-Allow-Origin 헤더로 응답한다.
  • 프리플라이트 요청(Preflight Request): PUT, DELETE 같은 복잡한 메서드나 커스텀 헤더(예: Authorization)를 사용하는 요청. 브라우저는 먼저 OPTIONS 요청을 보내 서버의 허용 여부를 확인한다.

2.2. 주요 CORS 헤더

  • Access-Control-Allow-Origin: 요청을 허용할 Origin(예: https://frontend.example.com 또는 *). 와일드카드(*)는 공개 API에 적합하지만, 인증이 필요한 경우 특정 Origin을 지정해야 한다.
  • Access-Control-Allow-Credentials: 쿠키나 인증 헤더를 포함한 요청을 허용할지 여부(true 또는 생략). 와일드카드(*)와 함께 사용할 수 없다.
  • Access-Control-Allow-Headers: 허용할 요청 헤더 목록(예: Authorization, Content-Type).
  • Access-Control-Allow-Methods: 허용할 HTTP 메서드(예: GET, POST, OPTIONS).
  • Access-Control-Max-Age: 프리플라이트 요청의 캐싱 기간(초 단위, 예: 86400).
  • Access-Control-Expose-Headers: 클라이언트가 접근 가능한 응답 헤더(예: Content-Length).

2.3. 데이터 엔지니어링에서의 CORS

  • API 호출: 데이터 시각화 대시보드(예: Tableau, Power BI)가 REST API에서 데이터를 가져올 때 CORS 설정이 필요하다.
  • 실시간 데이터 스트리밍: Apache Kafka나 AWS Kinesis와 연동된 프론트엔드에서 데이터를 요청할 때 CORS를 사용한다.
  • 클라우드 서비스: S3, Google Cloud Storage와의 통신에서 CORS 설정으로 안전한 데이터 액세스를 보장한다.


3. Nginx를 활용한 CORS 설정

Nginx는 웹 서버이자 리버스 프록시로, CORS 헤더를 설정하거나 프록시를 통해 SOP/CORS 문제를 해결할 수 있다. 데이터 엔지니어링에서는 Nginx를 프록시 서버로 사용하여 프론트엔드와 백엔드 간의 통신을 중개하거나, CORS 헤더를 추가하여 크로스-오리진 요청을 허용한다.

3.1. Nginx로 CORS 헤더 설정

다음은 Nginx에서 CORS를 활성화하는 기본 설정 예제이다. 이 설정은 https://frontend.example.com에서 오는 요청을 허용하며, 인증 정보와 커스텀 헤더를 지원한다.

server {
    listen 80;
    server_name api.example.com;

    location / {
        # 프리플라이트 요청 처리
        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Origin' 'https://frontend.example.com' always;
            add_header 'Access-Control-Allow-Credentials' 'true' always;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
            add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
            add_header 'Access-Control-Max-Age' 86400;
            add_header 'Content-Type' 'text/plain; charset=utf-8';
            add_header 'Content-Length' 0;
            return 204;
        }

        # 일반 요청 처리
        add_header 'Access-Control-Allow-Origin' 'https://frontend.example.com' always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;

        proxy_pass http://localhost:3000; # 백엔드 서버로 프록시
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
  • 설명:
    • if ($request_method = 'OPTIONS'): 프리플라이트 요청(OPTIONS)을 처리하며, 필요한 CORS 헤더를 추가하고 204 상태 코드를 반환한다.
    • always: 비정상 응답(예: 4xx, 5xx)에서도 헤더가 추가되도록 한다.
    • Access-Control-Allow-Credentials: true: 인증 정보(쿠키, Authorization 헤더)를 포함한 요청을 허용한다.
    • Access-Control-Max-Age: 86400: 프리플라이트 요청 결과를 24시간 동안 캐싱한다.
    • proxy_pass: 백엔드 서버(예: Node.js)로 요청을 전달한다.

3.2. Nginx 프록시로 SOP 우회

CORS 설정 대신 Nginx를 리버스 프록시로 사용하여 프론트엔드와 백엔드를 동일한 도메인으로 제공하면 SOP 문제를 완전히 우회할 수 있다. 예를 들어, 프론트엔드(https://example.com)와 백엔드(https://example.com/api)를 동일한 도메인 아래로 통합한다.

server {
    listen 80;
    server_name example.com;

    # 프론트엔드 정적 파일 제공
    location / {
        root /var/www/frontend;
        try_files $uri /index.html; # SPA(Single Page Application) 지원
    }

    # 백엔드 API로 프록시
    location /api {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
  • 설명:
    • 프론트엔드 요청(/)은 정적 파일(예: React 빌드 파일)을 제공한다.
    • 백엔드 요청(/api)은 로컬 백엔드 서버(포트 3000)로 전달된다.
    • 동일 도메인(example.com)을 사용하므로 SOP 제한이 적용되지 않는다.

3.3. 다중 Origin 지원

여러 도메인에서 API 요청을 허용하려면 Origin 헤더를 동적으로 처리한다.

map $http_origin $cors_header {
    default "";
    "~^https?://(frontend\.example\.com|app\.example\.com)$" $http_origin;
}

server {
    listen 80;
    server_name api.example.com;

    location / {
        add_header 'Access-Control-Allow-Origin' $cors_header always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;

        proxy_pass http://localhost:3000;
    }
}
  • 설명:
    • map 지시어를 사용하여 허용할 Origin을 정의한다.
    • $http_origin이 허용 목록에 없으면 빈 문자열로 설정되어 요청이 차단된다.


4. 보안 고려사항

  • 와일드카드 사용 자제: Access-Control-Allow-Origin: *는 공개 API에만 적합하다. 인증 정보가 포함된 요청에서는 특정 Origin을 지정해야 한다.
  • 헤더 검증: 허용되지 않은 헤더(예: 비표준 헤더)를 차단하여 보안을 강화한다.
  • CSRF 방지: CORS는 CSRF 공격을 직접 방지하지 않으므로, CSRF 토큰을 별도로 구현해야 한다.
  • 테스트: Postman, curl, 또는 CORS 테스트 도구(예: cors-test.codehappy.dev)를 사용해 설정을 검증한다.

5. 실제 예제: Python 소켓과 Nginx 연동

데이터 엔지니어링에서 TCP 소켓을 통해 데이터를 전송하고, Nginx로 CORS를 처리하는 간단한 예제를 제공한다.

Python 서버 코드

import socket

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('localhost', 3000))
server_socket.listen(1)

print("서버가 시작되었습니다...")
conn, addr = server_socket.accept()
print(f"연결된 클라이언트: {addr}")

data = conn.recv(1024).decode()
print(f"수신된 메시지: {data}")

conn.send("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n서버 응답".encode())
conn.close()
server_socket.close()

Nginx 설정

server {
    listen 80;
    server_name api.example.com;

    location / {
        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Origin' 'https://frontend.example.com' always;
            add_header 'Access-Control-Allow-Credentials' 'true' always;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
            add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;
            add_header 'Access-Control-Max-Age' 86400;
            return 204;
        }

        add_header 'Access-Control-Allow-Origin' 'https://frontend.example.com' always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        proxy_pass http://localhost:3000;
    }
}
  • 설명:
    • Python 서버는 TCP 소켓을 통해 데이터를 처리한다.
    • Nginx는 프록시로 요청을 받아 CORS 헤더를 추가하고 백엔드로 전달한다.

6. 결론

SOP와 CORS는 웹 애플리케이션의 보안과 데이터 접근을 관리하는 핵심 메커니즘이다. 데이터 엔지니어링에서는 API 호출, 데이터 스트리밍, 클라우드 통신에서 CORS 설정이 필수적이다. Nginx를 사용하면 CORS 헤더를 추가하거나 프록시를 통해 SOP를 우회할 수 있다. 올바른 설정을 통해 보안과 효율성을 모두 확보할 수 있으며, 데이터 파이프라인의 안정성을 높일 수 있다.

profile
Data Analytics Engineer 가 되

0개의 댓글