파이썬/Fast API

FastAPI Security: API 보안 구현

코샵 2025. 2. 24. 11:41
반응형
# 기본 설치 (FastAPI에 포함되어 있음)
pip install fastapi[all]
pip install python-jose[cryptography]  # JWT 토큰용
pip install passlib[bcrypt]  # 비밀번호 해싱용

OAuth2 비밀번호 인증

from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext

# 기본 설정
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

app = FastAPI()

@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user = authenticate_user(form_data.username, form_data.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password"
        )
    access_token = create_access_token(data={"sub": user.username})
    return {"access_token": access_token, "token_type": "bearer"}

API Key 인증

from fastapi.security import APIKeyHeader, APIKeyQuery

# 헤더 기반 API 키
api_key_header = APIKeyHeader(name="X-API-Key", auto_error=True)

# 쿼리 파라미터 기반 API 키
api_key_query = APIKeyQuery(name="api_key", auto_error=True)

@app.get("/secure/header")
async def secure_endpoint(api_key: str = Depends(api_key_header)):
    if not is_valid_api_key(api_key):
        raise HTTPException(status_code=403, detail="Invalid API Key")
    return {"message": "Secured by header"}

HTTP Basic 인증

from fastapi.security import HTTPBasic, HTTPBasicCredentials
import secrets

security = HTTPBasic()

@app.get("/users/me")
async def get_current_user(credentials: HTTPBasicCredentials = Depends(security)):
    correct_username = secrets.compare_digest(
        credentials.username, "admin"
    )
    correct_password = secrets.compare_digest(
        credentials.password, "secret"
    )
    if not (correct_username and correct_password):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid credentials",
            headers={"WWW-Authenticate": "Basic"},
        )
    return {"username": credentials.username}

JWT 토큰 구현

from datetime import datetime, timedelta
from typing import Optional

SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    expire = datetime.utcnow() + (
        expires_delta or timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    )
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

async def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
    except JWTError:
        raise credentials_exception
    return username

복합 보안 구현

from fastapi.security import OAuth2PasswordBearer, APIKeyHeader
from typing import Union

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False)

async def get_authorization(
    token: Union[str, None] = Depends(oauth2_scheme),
    api_key: Union[str, None] = Depends(api_key_header)
):
    if token:
        return await get_current_user(token)
    elif api_key:
        return verify_api_key(api_key)
    raise HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Invalid authentication credentials",
    )

@app.get("/secure")
async def secure_endpoint(auth = Depends(get_authorization)):
    return {"auth": auth}

CORS 설정

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

보안 미들웨어

from fastapi.middleware.trustedhost import TrustedHostMiddleware
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware

# HTTPS 리다이렉션
app.add_middleware(HTTPSRedirectMiddleware)

# 신뢰할 수 있는 호스트
app.add_middleware(
    TrustedHostMiddleware, 
    allowed_hosts=["example.com", "*.example.com"]
)

 

주요 기능 및 장점

  1. 다양한 인증 방식
    • OAuth2 지원
    • API 키 인증
    • Basic 인증
    • JWT 토큰
  2. 유연한 구성
    • 여러 인증 방식 조합
    • 커스텀 인증 로직
    • 세분화된 접근 제어
  3. 보안 기능
    • CORS 관리
    • HTTPS 강제
    • 호스트 검증
  4. 사용자 경험
    • 명확한 에러 메시지
    • 표준 인증 흐름
    • 문서화 지원

FastAPI.security를 사용하면 안전하고 표준을 준수하는 API 보안을 구현할 수 있습니다.