본문 바로가기
프로그램 (PHP,Python)

FastAPI 문서(Swagger·ReDoc·OpenAPI) 노출 엔드포인트 접근통제

by 날으는물고기 2026. 1. 21.

FastAPI 문서(Swagger·ReDoc·OpenAPI) 노출 엔드포인트 접근통제

728x90

FastAPI 문서화 기능 개요

FastAPI는 OpenAPI Specification(구 Swagger)을 중심으로 다음을 자동 생성합니다.

구성요소 기본 경로 역할
OpenAPI JSON /openapi.json API 명세 원본
Swagger UI /docs 대화형 API 테스트 UI
ReDoc /redoc 읽기 중심의 API 문서
OAuth2 지원 /docs 내부 인증 포함 API 테스트
Schema 자동 생성 - Request/Response 검증
예제 자동 노출 - Payload 예시

👉 코드 → 스키마 → 문서 → 테스트가 한 번에 연결되는 구조입니다.

OpenAPI (/openapi.json)

🔹 역할

  • FastAPI가 모든 라우트, 파라미터, 요청/응답 모델을 JSON 스펙으로 자동 변환
  • Swagger, ReDoc, 외부 도구(Postman, Stoplight, API Gateway)가 모두 이 파일을 사용

🔹 주요 구성

{
  "openapi": "3.1.0",
  "info": {
    "title": "My API",
    "version": "1.0.0"
  },
  "paths": {},
  "components": {
    "schemas": {},
    "securitySchemes": {}
  }
}

🔹 내부 동작

  • Python type hint + Pydantic 모델 기반
  • 런타임에 동적 생성 (static 파일 아님)

🔹 활용 사례

  • API Gateway 연동
  • 사내 API 표준 문서
  • SDK 자동 생성 (openapi-generator)

Swagger UI (/docs)

🔹 특징

  • 대화형 API 테스트
  • Authorization 헤더 설정 가능
  • 요청/응답 예제 자동 표시

🔹 제공 기능

  • Try it out
  • Request Body Schema
  • Response Code별 예시
  • OAuth2 로그인 버튼

🔹 OAuth2 연동 예시

from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

Swagger UI에서

  • 🔐 Authorize 버튼 자동 생성
  • Bearer Token 주입 가능

🔹 실무 활용

  • 프론트엔드/QA 협업
  • 테스트용 Mock API
  • 내부 운영 API 점검

ReDoc (/redoc)

🔹 특징

  • 읽기 중심 문서
  • 계층 구조 정리 우수
  • 비개발자/기획자 친화적

🔹 Swagger 대비 차이

항목 Swagger ReDoc
테스트 가능 불가
가독성 중간 높음
대규모 API 복잡 구조적
보안 노출 위험 높음 상대적으로 낮음

🔹 활용 사례

  • 외부 파트너 API 문서
  • 사내 API 포털
  • PDF 변환 문서

Pydantic Schema 자동 문서화

🔹 Request/Response 자동 노출

from pydantic import BaseModel

class User(BaseModel):
    id: int
    name: str
    is_active: bool = True

Swagger/ReDoc에 자동 표시

  • 필드 타입
  • 기본값
  • 필수 여부

🔹 예제 추가

class User(BaseModel):
    id: int
    name: str

    class Config:
        json_schema_extra = {
            "example": {
                "id": 1,
                "name": "admin"
            }
        }

API 메타데이터 커스터마이징

🔹 FastAPI 앱 설정

app = FastAPI(
    title="Internal Security API",
    description="보안 이벤트 수집 및 분석 API",
    version="2.1.0",
    contact={
        "name": "Security Team",
        "email": "sec@company.com"
    }
)

🔹 태그 기반 그룹화

@app.get("/alerts", tags=["Security"])

Docs/Schema 비활성화 및 경로 변경 (보안 핵심)

🔹 비활성화

app = FastAPI(
    docs_url=None,
    redoc_url=None,
    openapi_url=None
)

🔹 내부망 전용 경로 변경

app = FastAPI(
    docs_url="/_internal/docs",
    openapi_url="/_internal/openapi.json"
)

보안 관점 핵심 가이드

왜 위험한가?

  • API 전체 구조 노출
  • 인증 방식, 파라미터 구조 노출
  • 공격자에게 완벽한 공격 명세 제공

점검 포인트 체크리스트

✔ 운영 환경에서 /docs, /redoc 노출 여부
/openapi.json 외부 접근 가능 여부
✔ 인증 없는 Try-it-out 가능 여부
✔ 민감 필드(example 포함) 노출 여부
✔ OAuth 토큰 발급 API 노출 여부

300x250

권장 보안 정책 예시

환경 정책
DEV 전부 활성화
STG VPN + 인증
PROD 완전 비활성화 or IP 제한

인증 기반 Docs 접근 제어 예시

from fastapi import Depends, HTTPException, status

def docs_auth(token: str = Depends(oauth2_scheme)):
    if token != "internal-token":
        raise HTTPException(status_code=403)

@app.get("/_docs", dependencies=[Depends(docs_auth)])
def secured_docs():
    return get_swagger_ui_html(openapi_url="/openapi.json")

확장 활용 사례

  • API SDK 자동 생성
  • 테스트 자동화 (Postman/Newman)
  • AI API 분석 학습 데이터
  • 보안 점검 자동화 (OpenAPI Lint)
  • API 사용 정책 문서화

FastAPI의 docs/redoc/openapi는 “개발 생산성의 끝판왕”이지만,
운영 환경에서는 “가장 먼저 통제해야 할 공격 표면”입니다.

가장 권장: 네트워크 레벨 차단 (L7/L4)

방법

  • 내부망(VPC)에서만 접근
  • VPN/ZTNA 통해서만 접근
  • WAF / API Gateway / Ingress에서 /docs, /redoc, /openapi.json 경로를 차단 또는 allowlist
  • IP Allowlist 적용 (사내 공인IP, VPN 대역 등)

장점

  • 앱 코드 수정 최소화
  • 가장 강력하고 단순
  • 실수로 코드가 바뀌어도 외부 노출 방지

점검 포인트

  • /openapi.json도 동일하게 막혀 있는지 (문서만 막고 스펙은 열려 있는 실수 흔함)
  • 프록시 뒤에서 real ip 처리(X-Forwarded-For) 신뢰 설정이 안전한지

(예시) Nginx에서 경로 차단/허용

location ~ ^/(docs|redoc|openapi\.json)$ {
    allow 10.0.0.0/8;     # 내부망/VPN 대역
    allow 203.0.113.10;   # 특정 공인IP
    deny all;
    proxy_pass http://fastapi_upstream;
}

문서 기능 자체를 운영에서 끄기 (가장 확실)

운영에서는 아예 생성/제공을 비활성화하는 방식입니다.

app = FastAPI(
    docs_url=None,
    redoc_url=None,
    openapi_url=None
)

장점

  • 노출면(attack surface) 자체 제거
  • 설정 실수 가능성 감소

단점

  • 운영에서 문서가 필요하면 별도 대책 필요(스테이징에서만 제공 등)

경로 변경 + 비공개 네임스페이스로 숨기기 (보조책)

app = FastAPI(
    docs_url="/_internal/docs",
    redoc_url="/_internal/redoc",
    openapi_url="/_internal/openapi.json"
)

주의

  • “숨김”은 보안이 아닙니다.
    반드시 네트워크 제한 또는 인증과 함께 쓰는 보조책으로만 사용하세요.

인증 기반 접근제어 (앱 레벨)

HTTP Basic Auth (간단/효과적)

  • 내부 운영자가 빠르게 접근해야 할 때 현실적으로 많이 씁니다.
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi.openapi.docs import get_swagger_ui_html, get_redoc_html
import secrets, os

security = HTTPBasic()

DOC_USER = os.getenv("DOC_USER", "admin")
DOC_PASS = os.getenv("DOC_PASS", "change-me")

def basic_auth(credentials: HTTPBasicCredentials = Depends(security)):
    ok_user = secrets.compare_digest(credentials.username, DOC_USER)
    ok_pass = secrets.compare_digest(credentials.password, DOC_PASS)
    if not (ok_user and ok_pass):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            headers={"WWW-Authenticate": "Basic"},
        )

app = FastAPI(docs_url=None, redoc_url=None, openapi_url=None)

@app.get("/docs", dependencies=[Depends(basic_auth)], include_in_schema=False)
def docs():
    return get_swagger_ui_html(openapi_url="/openapi.json", title="Docs")

@app.get("/redoc", dependencies=[Depends(basic_auth)], include_in_schema=False)
def redoc():
    return get_redoc_html(openapi_url="/openapi.json", title="ReDoc")

@app.get("/openapi.json", dependencies=[Depends(basic_auth)], include_in_schema=False)
def openapi():
    return app.openapi()
포인트
  • docs_url=None로 기본 문서 라우트는 끄고, 내가 만든 라우트에 인증을 붙여 제공
  • include_in_schema=False로 이 라우트가 다시 문서에 노출되지 않게

JWT/OAuth2 기반 (조직 표준 SSO에 맞추기)

  • 사내 인증(SSO, OIDC) 붙여서 “관리자/개발자” 롤만 허용
핵심 원리
  • /docs, /redoc, /openapi.json 요청에 대해 Depends(verify_jwt_and_role) 같은 디펜던시로 검사
  • 역할(Role) 기반 접근제어(RBAC)
점검 포인트
  • 토큰이 브라우저에 저장될 경우(로컬스토리지 등) 탈취 위험
  • 문서 UI에서 “Try it out”이 가능한 경우 권한/감사로그(누가 어떤 호출 했는지) 고려

환경변수/배포환경 분리 (DEV/STG/PROD 정책)

운영에서 실수로 켜지는 사고를 막는 “조직 운영 정책”입니다.

import os
env = os.getenv("APP_ENV", "dev")

if env == "prod":
    app = FastAPI(docs_url=None, redoc_url=None, openapi_url=None)
else:
    app = FastAPI()
권장 운영정책(현실적인 베스트)
  • DEV: 전부 ON
  • STG: VPN/IP 제한 + 인증(권장)
  • PROD: 기본 OFF (정말 필요하면 내부망+인증으로 제한 제공)

“Try it out” 자체를 제한/통제하고 싶을 때

Swagger UI는 기본적으로 호출이 가능해서, 문서 접근권한이 곧 API 호출 인터페이스 제공이 됩니다.

통제 방법
  • 문서 접근은 허용하되, API 자체는
    • 운영망에서만 호출 가능
    • 또는 “읽기 전용 토큰”만 발급
    • 또는 문서용 계정은 특정 scope만 허용
점검 포인트
  • 문서 접근권한과 API 권한이 과도하게 묶이지 않게(최소권한)

실무 베스트 프랙티스

제일 추천(운영 안정/보안 강함)

  • PROD: docs/redoc/openapi 모두 OFF
  • STG: ON + VPN/IP allowlist + Basic Auth 또는 SSO
  • 필요 시 OpenAPI JSON은 CI에서 추출해 별도 문서포털로 배포

“운영에서도 꼭 봐야 한다”면

  • 경로 변경(/_internal/...)
  • + 네트워크 제한(IP/VPN)
  • + 앱 레벨 인증(Basic/SSO)
    ➡️ 3중으로 겹치기

보안 관점 체크리스트

  • 외부에서 /openapi.json 접근 가능한가?
  • /docs 막았는데 /redoc은 열려 있지 않은가?
  • 프록시/Ingress에서 우회 경로가 없는가? (rewrite)
  • 인증정보가 코드에 하드코딩되지 않았는가? (ENV/Secret)
  • 내부 접근 로그(누가 문서 접근/호출 했는지)가 남는가?
  • 문서에 예제로 토큰/키/내부 URL이 노출되지 않는가?
728x90
그리드형(광고전용)

댓글