nginx prod level 설정하기 (feat. Django)

강정우·2025년 7월 24일
0

네트워크

목록 보기
35/35
post-thumbnail

1. Django 설정

설정 중 DB 풀 설정, 로깅등은 제외하고 네트워크와 관련된 부분 알아보자.

1. 기본 및 핵심 설정

from .base import *
import os

DEBUG = False
ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', '').split(',')
SWAGGER_ENABLED = False

ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', '').split(','): Host header attacks 방어.

2. 보안 헤더 설정

# 보안 헤더 설정
SECURE_CROSS_ORIGIN_OPENER_POLICY = "same-origin"
SECURE_REFERRER_POLICY = 'strict-origin-when-cross-origin'
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_BROWSER_XSS_FILTER = True

웹 애플리케이션의 기본적인 보안을 강화

SECURE_CROSS_ORIGIN_OPENER_POLICY = "same-origin": 다른 출처의 팝업 창이 현재 문서에 접근하는 것을 제한한다.
Clickjacking과 같은 공격을 완화하는 데 도움이 된다.

SECURE_REFERRER_POLICY = 'strict-origin-when-cross-origin': Referrer 정보를 보낼 때의 정책을 정의한다.
다른 출처로 요청을 보낼 때만 오리진 정보를 포함하고, HTTPS에서 HTTP로 요청할 때는 보내지 않아 민감 정보 노출을 방지한다.

SECURE_CONTENT_TYPE_NOSNIFF = True: 브라우저가 MIME type sniffing을 통해 콘텐츠 타입을 유추하는 것을 방지한다.
이는 악의적인 콘텐츠가 잘못된 타입으로 해석되어 실행되는 것을 막는다.

SECURE_BROWSER_XSS_FILTER = True: 브라우저 내장 XSS(Cross-Site Scripting) 필터를 활성화하여 XSS 공격을 방어한다.

3. HTTPS 설정

# HTTPS 설정
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000  # 1년
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

Nginx에서 이미 HTTPS 리다이렉션과 HSTS를 처리하고 있으면 필요없음 하지만 애플리케이션 레벨에서도 설정하여 혹시 모를 누락을 방지

SECURE_HSTS_SECONDS = 31536000 # (1년): HSTS(HTTP Strict Transport Security)를 활성화.
브라우저가 특정 기간(여기서는 1년) 동안 이 도메인에 대한 모든 요청을 HTTPS로만 보내도록 강제.
이는 중간자 공격(Man-in-the-Middle)을 통한 SSL 스트리핑 공격을 방지.

SECURE_HSTS_INCLUDE_SUBDOMAINS = True: HSTS 정책을 모든 서브도메인에도 적용한다.

SECURE_HSTS_PRELOAD = True: HSTS Preload List에 등록될 자격을 부여한다.
Preload List에 등록되면, 사용자가 도메인에 최초 접속 시에도 HTTP 요청을 보내지 않고 바로 HTTPS로 연결을 시도하게 된다.

4. 세션 및 CSRF 보안

# 세션 보안
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Strict'  # 최고 보안

# CSRF 보안
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True
CSRF_COOKIE_SAMESITE = 'Strict'  # 최고 보안

쿠키를 통한 세션 및 CSRF 토큰 관리에 대한 보안 강화

SESSION_COOKIE_SECURE = True: 세션 쿠키가 HTTPS 연결을 통해서만 전송되도록

SESSION_COOKIE_HTTPONLY = True: 자바스크립트가 세션 쿠키에 접근하는 것을 방지.
XSS 공격 시 쿠키 탈취를 어렵게 만듦

SESSION_COOKIE_SAMESITE = 'Strict': CSRF(Cross-Site Request Forgery) 공격을 방지하는 강력한 방법
'Strict' 모드는 동일 사이트 요청(Same-site request)에서만 쿠키가 전송되도록 한다.
(예: 다른 사이트에서 링크를 통해 접속하더라도 해당 사이트의 쿠키는 전송되지 않음.)
이는 보안을 크게 강화하지만, 경우에 따라 Lax 모드가 더 적합할 수도 있음.

CSRF_COOKIE_SECURE = True, CSRF_COOKIE_HTTPONLY = True, CSRF_COOKIE_SAMESITE = 'Strict': CSRF 토큰 쿠키에도 세션 쿠키와 동일한 강력한 보안 설정이 적용

5. CORS 설정

# CORS 설정 (프로덕션 - 제한적)
CORS_ALLOW_ALL_ORIGINS = False
CORS_ALLOWED_ORIGINS = os.getenv('CORS_ALLOWED_ORIGINS', '').split(',')
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_METHODS = ( ... )
CORS_ALLOW_HEADERS = ( ... )

CORS_ALLOW_ALL_ORIGINS = False: 모든 출처에서의 CORS 요청을 허용하지 않음.

2. nginx 설정

보통은 /api/ 로 경로를 나누어 요청하는 것이 일반적이지만 팀원의 요청으로 인하여 :8000 로 분기처리를 하였다.

server {
    listen 80;
    listen [::]:80;
    server_name cnn.parking.monster;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name cnn.parking.monster;

    # SSL 설정 (참고로 아래 경로들은 cerbot 를 사용했다면 기본 경로들)
    ssl_certificate /etc/letsencrypt/live/[도메인 네임]/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/[도메인 네임]/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    # 보안 헤더 (옵션, 권장)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    add_header X-Content-Type-Options nosniff always;
    add_header X-Frame-Options DENY always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # 파일 업로드 크기 제한 (옵션)
    client_max_body_size 10M;
    
    # 타임아웃 설정 (옵션)
    proxy_connect_timeout 60s;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;

    # API 엔드포인트 (옵션 -> 여기선 8000 포트로 나눔.)
    location /api/ {
        proxy_pass http://unix:/run/gunicorn.sock;
        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;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;
        
        # WebSocket 지원 (옵션)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        
        # 버퍼링 설정 (옵션)
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 4k;
    }

    # 정적 파일 서빙
    location / {
        root [build 파일 위치];
        try_files $uri $uri/ /index.html;
        
        # 404 에러 처리
        error_page 404 /index.html;

        # HTML 파일 캐싱 방지
        location ~* \.html$ {
            expires -1;
            add_header Cache-Control "no-cache, no-store, must-revalidate, proxy-revalidate";
            add_header Pragma "no-cache";
            add_header Last-Modified $date_gmt;
            add_header ETag "";
            if_modified_since off;
        }

        # 정적 자원 캐싱
        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|webp|woff|woff2|ttf|eot|map)$ {
            expires 1y;
            add_header Cache-Control "public, immutable";
            add_header Vary "Accept-Encoding";
            
            # Gzip 압축 활성화 (옵션)
            gzip_static on;
        }
    }

    # 로그 설정
    access_log /var/log/nginx/cnn.parking.monster.access.log;
    error_log /var/log/nginx/cnn.parking.monster.error.log;
}

server {
    listen 8000 ssl http2;
    listen [::]:8000 ssl http2;
    server_name [도메인 네임];

    # SSL 설정
    ssl_certificate /etc/letsencrypt/live/[도메인 네임]/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/[도메인 네임]/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    # 보안 헤더
    add_header X-Content-Type-Options nosniff always;
    add_header X-Frame-Options DENY always;

    # 파일 업로드 크기 제한 (옵션)
    client_max_body_size 10M;
    
    # 타임아웃 설정
    proxy_connect_timeout 60s;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;

    location / {
        proxy_pass http://unix:/run/gunicorn.sock;
        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;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;
        
        # HTTP 버전 및 버퍼링 설정
        proxy_http_version 1.1;
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 4k;
    }

    # 로그 설정
    access_log /var/log/nginx/cnn.parking.monster.8000.access.log;
    error_log /var/log/nginx/cnn.parking.monster.8000.error.log;
}
profile
智(지)! 德(덕)! 體(체)!

0개의 댓글