6. 보안 베이스라인: 헤더/접근제어/타임아웃/업로드 제한 “최소 세트”
2025. 12. 28. 20:59
TechNote · NGINX · 06

보안 베이스라인: “운영 포털”을 안전하게 기본 세팅하는 최소 세트

운영 포털을 안전하게 운영하기 위해, 꼭 필요한 최소 보안 기본값(헤더·TLS·정책·로그)을 한 번에 정리해 드립니다.

이 글의 목표
운영 포털/대시보드는 “내부망이라 괜찮다”가 아니라,
기본 보안 헤더 + 접근 제한 + 요청 제한 + 안전한 TLS
프록시 레벨에서 표준화해서 사고 확률을 낮추는 겁니다.

1) HTTPS 강제 + HSTS

1-1) 80 → 443 리다이렉트

server {
  listen 80;
  server_name portal.example.com;
  return 301 https://$host$request_uri;
}

1-2) HSTS(주의해서 적용)

HSTS는 브라우저에게 “앞으로는 HTTPS로만 접속하라”는 정책을 심어줍니다. 운영자 입장에선 보안 효과가 크지만, 잘못 적용하면 복구가 어렵습니다(특히 preload). :contentReference[oaicite:13]{index=13}

# HTTPS server{} 안에
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
운영자 팁
add_header는 상태코드에 따라 적용이 누락될 수 있어 always를 습관처럼 붙이는 게 안전합니다.

2) 보안 헤더 기본 세트(add_header always)

운영 포털 보안 베이스라인(레이어 개념도)
운영 포털 보안 베이스라인(레이어 개념도)

운영 포털의 “기본 방어선”으로 아래를 추천합니다.

# /etc/nginx/conf.d/60-security-headers.conf

add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "no-referrer" always;

# 필요 시(조직 정책/서비스 특성에 맞게 조정)
# add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

3) CSP는 “Report-Only → 점진 적용”이 정답

CSP(Content-Security-Policy)는 XSS/리소스 로딩을 강력하게 제한합니다. 하지만 운영 포털은 SPA/외부 CDN/내부 API가 섞여 있어서, 처음부터 강하게 걸면 화면이 깨지기 쉽습니다.

3-1) 먼저 Report-Only로 관측부터

# 우선은 Report-Only로 시작(깨짐 방지)
add_header Content-Security-Policy-Report-Only "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self';" always;

3-2) 안정화되면 enforce로 전환

# 안정화 후에만!
add_header Content-Security-Policy "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self';" always;

참고: CSP는 add_header로 설정하는 게 일반적인 패턴입니다. 

4) 접근 제어: allow/deny로 “관리망만 허용”

내부 운영 포털은 외부에 열지 않더라도, 접근 가능한 대역을 최소화하는 것만으로 사고 확률이 크게 줄어듭니다. allow/deny는 NGINX 표준 모듈로 지원됩니다.

# 예: 특정 대역만 허용
location / {
  allow 10.0.0.0/8;
  allow 192.168.0.0/16;
  deny  all;

  # 통과한 요청만 upstream으로
  proxy_pass http://127.0.0.1:3000;
}
운영자 팁
“인증(RBAC)”이 있어도, 접근 대역을 줄이면 공격 표면이 더 줄어듭니다.
둘 중 하나만 하라고 하면? 가능하면 둘 다.

5) 요청/업로드/타임아웃 제한(운영자 실전)

5-1) 업로드 제한(대시보드/정책 자동화에서 필수)

# server{} 또는 location /api/ {} 안에
client_max_body_size 50m;

5-2) 타임아웃(느린 클라/느린 백엔드에서 “좀비 연결” 방지)

# 클라이언트가 헤더를 너무 늦게 보내면 끊기
client_header_timeout 15s;

# 백엔드가 오래 걸릴 수 있는 작업(리포트 생성 등)
proxy_read_timeout 300s;
proxy_send_timeout 300s;

참고: client_header_timeout 같은 코어 타임아웃은 http/server 레벨에서 사용합니다. 

5-3) Rate limit(로그인/토큰 경로 최소 방어)

레이트리밋은 NGINX 표준 모듈(leaky bucket)로 제공됩니다.

# http{} 레벨
limit_req_zone $binary_remote_addr zone=login_zone:10m rate=5r/s;

# login/인증 관련 경로
location /oauth2/ {
  limit_req zone=login_zone burst=10 nodelay;
  proxy_pass http://127.0.0.1:4180;
}

6) 검증 커맨드(체크리스트)

# 1) 헤더 확인(HSTS/CSP/기본헤더)
curl -kI https://portal.example.com/ | egrep -i 'strict-transport|content-security|x-frame|x-content-type|referrer'

# 2) http → https 리다이렉트 확인
curl -I http://portal.example.com/ | egrep -i 'HTTP/|Location'

# 3) 접근제어 확인(허용되지 않은 네트워크에서 403/444 등)
curl -kI https://portal.example.com/ | egrep -i 'HTTP/'