본문 바로가기
01. AI 테크 & 트렌드 (Main)/Claude & Anthropic

[보안포털 구축기 2편]보안포털 코드 보안 강화 — 7가지 취약점 수정기

by 몽블86 2026. 4. 18.

 

보안포털 코드 보안 강화 — 7가지 취약점 수정기

FastAPI로 구축한 내부 보안 관리 포털을 운영하면서 코드 리뷰를 진행했습니다.
정보보안 전문가 입장에서 직접 취약점을 찾고 수정한 과정을 공유합니다.
초보 개발자도 이해할 수 있도록 쉽게 설명했어요.

📋 발견된 취약점 목록

코드 리뷰 결과 총 7가지 문제를 발견했습니다.

번호 항목 심각도
1 CORS 전체 개방 🔴 심각
2 JWT 토큰 만료시간 8시간 🔴 심각
3 JWT 서명키 fallback 설정 🔴 심각
4 DB 연결 풀 미설정 🟡 주의
5 DB 오류 시 rollback 없음 🟡 주의
6 deprecated API 사용 🟡 주의
7 테스트 fixture 중복 가능성 🟡 주의

1️⃣ CORS 전체 개방 (🔴 심각)

CORS가 뭔가요?

브라우저가 다른 도메인의 API 호출을 제어하는 보안 정책이에요. allow_origins=["*"]로 설정하면 어떤 웹사이트에서든 우리 API를 호출할 수 있습니다.

# 수정 전 (위험)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],   # 모든 도메인 허용
    allow_methods=["*"],
    allow_headers=["*"],
)
 
# 수정 후 (안전)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://[서버주소]"],  # 특정 도메인만 허용
    allow_methods=["GET", "POST", "PUT", "DELETE"],
    allow_headers=["Authorization", "Content-Type"],
)
💡 쉽게 말하면: 현관문을 아무나 열 수 있게 뒀다가, 우리 가족만 열 수 있도록 잠금장치를 설치한 것.
⚠️ 주의: CORS는 IP 접속 차단이 아닙니다. 브라우저 단에서 동작하므로 사용자의 직접 접속에는 영향을 주지 않아요.

2️⃣ JWT 토큰 만료시간 8시간 (🔴 심각)

JWT 토큰은 로그인 후 발급되는 출입증이에요. 만료시간이 길수록 토큰이 탈취됐을 때 피해가 커집니다.

# 수정 전 (위험)
ACCESS_TOKEN_EXPIRE_MINUTES = 480  # 8시간
 
# 수정 후 (안전)
ACCESS_TOKEN_EXPIRE_MINUTES = 30   # 30분
💡 쉽게 말하면: 회사 출입증 유효기간을 8시간에서 30분으로 단축. 출입증이 도난당해도 30분 후엔 자동 만료됩니다.
💡 보안 포털, 금융, 의료 시스템은 15~30분이 업계 표준입니다.

3️⃣ JWT 서명키 fallback 설정 (🔴 심각)

환경변수가 없을 때 기본값으로 동작하는 코드는 매우 위험합니다. 누구나 알 수 있는 키로 토큰을 위조할 수 있어요.

# 수정 전 (위험)
SECRET_KEY = os.getenv('SECRET_KEY', 'fallback-key')
# .env 파일 없으면 'fallback-key'로 JWT 서명
# 누구나 이 키로 토큰 위조 가능
 
# 수정 후 (안전)
SECRET_KEY = os.getenv('SECRET_KEY')
if not SECRET_KEY:
    raise RuntimeError('SECRET_KEY 환경변수가 설정되지 않았습니다')
# 키 없으면 서버 자체가 실행되지 않음
🚨 쉽게 말하면: 마스터키를 잃어버리면 만능키로 열리게 해뒀다가, 마스터키 없으면 문 자체가 잠기도록 변경.

4️⃣ DB 연결 풀 미설정 (🟡 주의)

DB 연결 풀이란 미리 연결을 여러 개 만들어두고 재사용하는 방식이에요. 설정이 없으면 동시 접속자가 많을 때 DB 연결이 부족해질 수 있습니다.

# 수정 전
engine = create_engine(DATABASE_URL)
# 기본값 사용 → 동시 접속자 많으면 DB 연결 부족
 
# 수정 후
engine = create_engine(
    DATABASE_URL,
    pool_size=10,       # 기본 연결 수
    max_overflow=20,    # 최대 추가 연결
    pool_timeout=30,    # 연결 대기 최대 시간(초)
    pool_recycle=1800,  # 30분마다 연결 갱신
)
💡 쉽게 말하면: 은행 창구를 기본 10개 열어두고, 사람이 몰리면 최대 20개까지 추가로 여는 방식.

5️⃣ DB 오류 시 rollback 없음 (🟡 주의)

DB 작업 중 오류가 나면 반드시 작업을 취소(rollback)해야 데이터가 꼬이지 않아요.

# 수정 전
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()  # 오류 발생해도 그냥 닫음
 
# 수정 후
def get_db():
    db = SessionLocal()
    try:
        yield db
    except Exception:
        db.rollback()  # 오류 시 작업 먼저 취소
        raise
    finally:
        db.close()     # 그 다음 닫음
💡 쉽게 말하면: 은행 거래 중 오류나면 그냥 창구를 닫았다가, 이제는 거래를 먼저 취소한 후 창구를 닫도록 변경. 데이터 꼬임 방지.

6️⃣ deprecated API 사용 (🟡 주의)

Python 3.12부터 datetime.utcnow()는 deprecated(구식)로 경고가 발생합니다.

# 수정 전 (경고 발생)
from datetime import datetime, timedelta
expire = datetime.utcnow() + timedelta(...)
 
# 수정 후 (최신 표준)
from datetime import datetime, timedelta, timezone
expire = datetime.now(timezone.utc) + timedelta(...)
💡 쉽게 말하면: 구형 시계를 신형 시계로 교체. 기능은 같지만 최신 표준을 따릅니다.

7️⃣ 테스트 fixture 중복 문제 (🟡 주의)

테스트를 반복 실행할 때 같은 사용자명으로 DB에 insert하면 중복 오류가 발생할 수 있어요.

# 수정 전 (중복 오류 가능)
user = User(
    username="testuser",          # 항상 같은 이름 → 중복 오류
    email="test@company.com"
)
 
# 수정 후 (중복 방지)
import uuid
unique = uuid.uuid4().hex[:8]     # 랜덤 8자리 생성
user = User(
    username=f"testuser_{unique}",        # testuser_a3f9b2c1
    email=f"test_{unique}@company.com"   # 매번 고유한 이메일
)
💡 쉽게 말하면: 테스트할 때마다 같은 이름의 직원을 등록하려다 오류나던 것을, 매번 다른 사번으로 등록하도록 변경.

✅ 수정 결과

7개 취약점 수정 후 기존 테스트 12개 전부 통과를 확인했습니다.

$ pytest tests/ --no-cov

............

======================== 12 passed, 4 warnings ========================

🏗️ 현재 VM 아키텍처

외부 인터넷
    ↓
Nginx (80/443포트) ← 관문 역할
    ├── 보안포털   → FastAPI (uvicorn, 내부 포트)
    └── 다른서비스 → Docker 컨테이너 (격리됨)
 
내부망
    ├── FastAPI (uvicorn)    ← 보안포털 백엔드
    └── PostgreSQL (내부만) ← 외부 직접 접근 불가
💡 보안 포인트: PostgreSQL은 외부에서 직접 접근 불가능합니다. 내부에서만 접근할 수 있어요.

🐳 다음 계획 — Docker 전환

현재 FastAPI가 systemd로 직접 실행되고 있는데, 보안 강화를 위해 Docker 컨테이너로 전환할 예정입니다.

현재 구성 도커 전환 후
systemd로 FastAPI 직접 실행 컨테이너 A (FastAPI)
직접 설치된 PostgreSQL 컨테이너 B (PostgreSQL)
직접 설치된 Nginx 컨테이너 C (Nginx)
도커 전환 기대 효과 설명
프로세스 격리 하나가 뚫려도 다른 서비스 안전
네트워크 격리 컨테이너 간 통신만 허용
버전 관리 이미지로 롤백 용이
배포 자동화 docker-compose로 한 번에 관리
🚀 다음 포스팅에서 Docker 전환 과정을 상세히 공유할 예정입니다!

📝 마치며

코드 리뷰는 개발 후 선택이 아니라 필수입니다. 특히 보안 시스템이라면 더욱 그렇죠.

오늘 수정한 7가지 중 🔴 심각 3개는 실제 운영 중인 시스템에서 흔히 발견되는 취약점입니다. 여러분의 코드도 한 번 점검해보세요!