콘텐츠로 이동

엄격한 Content-Type 확인

🌐 AI와 사람이 함께한 번역

이 번역은 사람의 안내를 받아 AI가 만들었습니다. 🤝

원문의 의미를 오해하거나 부자연스러워 보이는 등 오류가 있을 수 있습니다. 🤖

AI LLM을 더 잘 안내하는 데 도움을 주세요.

영문 버전

기본적으로 FastAPI는 JSON 요청 본문에 대해 엄격한 Content-Type 헤더 검사를 사용합니다. 이는 JSON 요청의 본문을 JSON으로 파싱하려면 유효한 Content-Type 헤더(예: application/json)를 반드시 포함해야 함을 의미합니다.

CSRF 위험

이 기본 동작은 매우 특정한 시나리오에서 Cross-Site Request Forgery (CSRF) 공격의 한 유형에 대한 보호를 제공합니다.

이러한 공격은 브라우저가 다음과 같은 경우 CORS 사전 요청(preflight) 검사를 수행하지 않고 스크립트가 요청을 보내도록 허용한다는 점을 악용합니다:

  • Content-Type 헤더가 없음(예: Blob 본문과 함께 fetch() 사용)
  • 그리고 어떠한 인증 자격 증명도 보내지 않음

이 유형의 공격은 주로 다음과 같은 경우에 관련이 있습니다:

  • 애플리케이션이 로컬(예: localhost) 또는 내부 네트워크에서 실행 중이고
  • 애플리케이션에 인증이 없어 같은 네트워크에서 오는 모든 요청을 신뢰한다고 가정하는 경우

공격 예시

로컬 AI 에이전트를 실행하는 방법을 만들었다고 가정해 봅시다.

이 에이전트는 다음 위치에 API를 제공합니다:

http://localhost:8000/v1/agents/multivac

또한 다음 위치에 프론트엔드가 있습니다:

http://localhost:8000

두 주소 모두 같은 호스트를 사용합니다.

그런 다음 프론트엔드를 통해 AI 에이전트가 여러분을 대신해 작업을 수행하도록 할 수 있습니다.

이는 공개 인터넷이 아니라 로컬에서 실행되므로, 여러분은 로컬 네트워크 접근만을 신뢰하고 별도의 인증을 설정하지 않기로 합니다.

그 후 사용자는 이를 설치해 로컬에서 실행할 수 있습니다.

그리고 다음과 같은 악성 웹사이트를 열 수 있습니다:

https://evilhackers.example.com

그 악성 웹사이트가 Blob 본문으로 fetch()를 사용해 로컬 API로 요청을 보냅니다:

http://localhost:8000/v1/agents/multivac

악성 웹사이트의 호스트와 로컬 앱의 호스트가 다르더라도, 브라우저는 다음과 같은 이유로 CORS 사전 요청(preflight)을 트리거하지 않습니다:

  • 인증 없이 동작하므로 자격 증명을 보낼 필요가 없습니다.
  • 브라우저는 JSON을 보내지 않는다고 판단합니다(Content-Type 헤더가 없기 때문).

그러면 악성 웹사이트가 로컬 AI 에이전트로 하여금 사용자의 전 직장 상사에게 화난 메시지를 보내게 하거나... 더 나쁜 일을 시킬 수도 있습니다. 😅

공개 인터넷

여러분의 앱이 공개 인터넷에 있다면, '네트워크를 신뢰'하여 누구나 인증 없이 권한 있는 요청을 보내도록 두지는 않을 것입니다.

공격자는 브라우저 상호작용 없이도 스크립트를 실행해 여러분의 API로 요청을 보낼 수 있으므로, 아마 이미 권한이 필요한 엔드포인트는 보호하고 있을 것입니다.

그런 경우에는 이 공격/위험은 해당하지 않습니다.

이 위험과 공격은 주로 앱이 로컬 네트워크에서 실행되고 그것이 유일한 보호수단이라고 가정할 때 관련이 있습니다.

Content-Type 없이 요청 허용하기

만약 Content-Type 헤더를 보내지 않는 클라이언트를 지원해야 한다면, strict_content_type=False로 설정해 엄격한 검사를 비활성화할 수 있습니다:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI(strict_content_type=False)


class Item(BaseModel):
    name: str
    price: float


@app.post("/items/")
async def create_item(item: Item):
    return item

이 설정을 사용하면 Content-Type 헤더가 없는 요청도 본문이 JSON으로 파싱됩니다. 이는 이전 버전의 FastAPI와 동일한 동작입니다.

정보

이 동작과 설정은 FastAPI 0.132.0에 추가되었습니다.