Study/fastapi

FastAPI에서 CSRF(Cross-Site Request Forgery) 적용하는 방법

bluebamus 2025. 1. 15.

1. 자체적으로 CSRF 보호 구현하기
   - FastAPI에서는 기본적으로 CSRF를 제공하지 않기 때문에, 이를 구현하려면 요청의 유효성을 직접 체크해야 한다.

 

   - CSRF 보호를 구현하는 일반적인 방법:
      - CSRF Token 생성: 사용자가 로그인하거나 세션을 시작할 때 CSRF 토큰을 생성한다.

         - 이 토큰은 세션 쿠키나 HTTP 헤더를 통해 클라이언트에게 전달된다.
      - CSRF Token 검증: 클라이언트가 서버에 요청을 보낼 때 CSRF 토큰을 포함시켜야 하며, 서버는 해당 토큰이 유효한지 검증한다.

         - 이 검증은 보통 요청의 Authorization 헤더나 X-CSRF-Token과 같은 커스텀 헤더에 토큰을 포함시키는 방식으로 처리된다.

 

   1) 자체 구현 예시:
      - CSRF 토큰 생성:
         - 서버는 사용자가 로그인할 때 세션에 CSRF 토큰을 저장한다.
         - 이 토큰을 HTML 페이지나 API 응답에서 클라이언트에게 전달한다.

      - CSRF 토큰 검증:
         - 클라이언트는 POST 요청을 보낼 때 해당 토큰을 X-CSRF-Token 헤더에 포함시킵니다.
         - 서버는 이 토큰을 요청 헤더에서 가져와 세션에 저장된 토큰과 비교합니다.

from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import HTMLResponse
import secrets

app = FastAPI()

# CSRF 토큰을 세션에 저장하는 임시 방법
csrf_tokens = {}

@app.get("/", response_class=HTMLResponse)
async def get_form(request: Request):
    # CSRF 토큰 생성
    csrf_token = secrets.token_hex(32)
    csrf_tokens[csrf_token] = True  # 세션에 토큰 저장 (임시 방식)
    
    # HTML 폼에 CSRF 토큰 포함
    return f"""
    <form method="POST" action="/submit">
        <input type="hidden" name="csrf_token" value="{csrf_token}">
        <input type="text" name="username">
        <input type="submit">
    </form>
    """

@app.post("/submit")
async def submit_form(request: Request):
    form = await request.form()
    csrf_token = form.get("csrf_token")
    
    # CSRF 토큰 검증
    if not csrf_tokens.get(csrf_token):
        raise HTTPException(status_code=400, detail="Invalid CSRF token")
    
    # 요청 처리 (예: 사용자 이름 저장)
    username = form.get("username")
    return {"message": f"Hello, {username}!"}

 

2. CSRF 보호를 위한 라이브러리 사용

   - FastAPI에서 CSRF 보호를 손쉽게 적용하려면 외부 라이브러리를 사용하는 방법이 더 효율적이다.

   - 대표적으로 사용할 수 있는 라이브러리는 starlette_csrf와 같은 CSRF 보호 라이브러리이다.
   - starlette_csrf: starlette_csrf는 Starlette 기반의 CSRF 보호 미들웨어로, FastAPI에서도 사용할 수 있다. 이 라이브러리는 CSRF 토큰을 자동으로 생성하고, 요청에서 이를 검증해준다.

 

   1) 설치

pip install starlette_csrf

 

   2) 미들웨어로 추가

from fastapi import FastAPI, Depends
from starlette.middleware.csrf import CSRFMiddleware

app = FastAPI()

# CSRF 미들웨어 추가
app.add_middleware(CSRFMiddleware, secret="your-secret-key")

@app.post("/submit")
async def submit_form(csrf_token: str = Depends(CSRFMiddleware.csrf_token)):
    # CSRF 토큰 검증을 자동으로 처리
    return {"message": "CSRF token is valid"}

 

댓글